Index: ps/trunk/source/graphics/MapGenerator.cpp =================================================================== --- ps/trunk/source/graphics/MapGenerator.cpp +++ ps/trunk/source/graphics/MapGenerator.cpp @@ -115,11 +115,10 @@ bool CMapGeneratorWorker::Run() { - JSContext* cx = m_ScriptInterface->GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(m_ScriptInterface); // Parse settings - JS::RootedValue settingsVal(cx); + JS::RootedValue settingsVal(rq.cx); if (!m_ScriptInterface->ParseJSON(m_Settings, &settingsVal) && settingsVal.isUndefined()) { LOGERROR("CMapGeneratorWorker::Run: Failed to parse settings"); @@ -144,7 +143,7 @@ RegisterScriptFunctions_MapGenerator(); // Copy settings to global variable - JS::RootedValue global(cx, m_ScriptInterface->GetGlobalObject()); + JS::RootedValue global(rq.cx, m_ScriptInterface->GetGlobalObject()); if (!m_ScriptInterface->SetProperty(global, "g_MapSettings", settingsVal, true, true)) { LOGERROR("CMapGeneratorWorker::Run: Failed to define g_MapSettings"); @@ -326,10 +325,9 @@ } CMapGeneratorWorker* self = static_cast(pCxPrivate->pCBData); - JSContext* cx = self->m_ScriptInterface->GetContext(); - JSAutoRequest rq(cx); - JS::RootedValue returnValue(cx); - ToJSVal_vector(cx, &returnValue, heightmap); + ScriptInterface::Request rq(self->m_ScriptInterface); + JS::RootedValue returnValue(rq.cx); + ToJSVal_vector(rq, &returnValue, heightmap); return returnValue; } @@ -337,8 +335,7 @@ JS::Value CMapGeneratorWorker::LoadMapTerrain(ScriptInterface::CxPrivate* pCxPrivate, const VfsPath& filename) { CMapGeneratorWorker* self = static_cast(pCxPrivate->pCBData); - JSContext* cx = self->m_ScriptInterface->GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(self->m_ScriptInterface); if (!VfsFileExists(filename)) { @@ -400,10 +397,10 @@ } } - JS::RootedValue returnValue(cx); + JS::RootedValue returnValue(rq.cx); ScriptInterface::CreateObject( - cx, + rq, &returnValue, "height", heightmap, "textureNames", textureNames, Index: ps/trunk/source/graphics/MapReader.cpp =================================================================== --- ps/trunk/source/graphics/MapReader.cpp +++ ps/trunk/source/graphics/MapReader.cpp @@ -371,15 +371,14 @@ void CMapSummaryReader::GetMapSettings(const ScriptInterface& scriptInterface, JS::MutableHandleValue ret) { - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(scriptInterface); - ScriptInterface::CreateObject(cx, ret); + ScriptInterface::CreateObject(rq, ret); if (m_ScriptSettings.empty()) return; - JS::RootedValue scriptSettingsVal(cx); + JS::RootedValue scriptSettingsVal(rq.cx); scriptInterface.ParseJSON(m_ScriptSettings, &scriptSettingsVal); scriptInterface.SetProperty(ret, "settings", scriptSettingsVal, false); } @@ -1267,8 +1266,7 @@ int CMapReader::GenerateMap() { - JSContext* cx = pSimulation2->GetScriptInterface().GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(pSimulation2->GetScriptInterface()); if (!m_MapGen) { @@ -1300,7 +1298,7 @@ shared_ptr results = m_MapGen->GetResults(); // Parse data into simulation context - JS::RootedValue data(cx); + JS::RootedValue data(rq.cx); pSimulation2->GetScriptInterface().ReadStructuredClone(results, &data); if (data.isUndefined()) @@ -1310,7 +1308,7 @@ } else { - m_MapData.init(cx, data); + m_MapData.init(rq.cx, data); } } else @@ -1330,8 +1328,7 @@ int CMapReader::ParseTerrain() { TIMER(L"ParseTerrain"); - JSContext* cx = pSimulation2->GetScriptInterface().GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(pSimulation2->GetScriptInterface()); // parse terrain from map data // an error here should stop the loading process @@ -1365,7 +1362,7 @@ // build tile data m_Tiles.resize(SQR(size)); - JS::RootedValue tileData(cx); + JS::RootedValue tileData(rq.cx); GET_TERRAIN_PROPERTY(m_MapData, tileData, &tileData) // parse tile data object into flat arrays @@ -1406,8 +1403,7 @@ int CMapReader::ParseEntities() { TIMER(L"ParseEntities"); - JSContext* cx = pSimulation2->GetScriptInterface().GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(pSimulation2->GetScriptInterface()); // parse entities from map data std::vector entities; @@ -1471,14 +1467,13 @@ int CMapReader::ParseEnvironment() { // parse environment settings from map data - JSContext* cx = pSimulation2->GetScriptInterface().GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(pSimulation2->GetScriptInterface()); #define GET_ENVIRONMENT_PROPERTY(val, prop, out)\ if (!pSimulation2->GetScriptInterface().GetProperty(val, #prop, out))\ LOGWARNING("CMapReader::ParseEnvironment() failed to get '%s' property", #prop); - JS::RootedValue envObj(cx); + JS::RootedValue envObj(rq.cx); GET_ENVIRONMENT_PROPERTY(m_MapData, Environment, &envObj) if (envObj.isUndefined()) @@ -1511,10 +1506,10 @@ m_LightEnv.m_UnitsAmbientColor = RGBColor(unitsAmbientColor.r, unitsAmbientColor.g, unitsAmbientColor.b); // Water properties - JS::RootedValue waterObj(cx); + JS::RootedValue waterObj(rq.cx); GET_ENVIRONMENT_PROPERTY(envObj, Water, &waterObj) - JS::RootedValue waterBodyObj(cx); + JS::RootedValue waterBodyObj(rq.cx); GET_ENVIRONMENT_PROPERTY(waterObj, WaterBody, &waterBodyObj) // Water level - necessary @@ -1538,7 +1533,7 @@ GET_ENVIRONMENT_PROPERTY(waterBodyObj, WindAngle, pWaterMan->m_WindAngle) } - JS::RootedValue fogObject(cx); + JS::RootedValue fogObject(rq.cx); GET_ENVIRONMENT_PROPERTY(envObj, Fog, &fogObject); GET_ENVIRONMENT_PROPERTY(fogObject, FogFactor, m_LightEnv.m_FogFactor); @@ -1548,7 +1543,7 @@ GET_ENVIRONMENT_PROPERTY(fogObject, FogColor, fogColor); m_LightEnv.m_FogColor = RGBColor(fogColor.r, fogColor.g, fogColor.b); - JS::RootedValue postprocObject(cx); + JS::RootedValue postprocObject(rq.cx); GET_ENVIRONMENT_PROPERTY(envObj, Postproc, &postprocObject); std::wstring postProcEffect; @@ -1571,8 +1566,8 @@ int CMapReader::ParseCamera() { - JSContext* cx = pSimulation2->GetScriptInterface().GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(pSimulation2->GetScriptInterface()); + // parse camera settings from map data // defaults if we don't find player starting camera float declination = DEGTORAD(30.f), rotation = DEGTORAD(-45.f); @@ -1582,7 +1577,7 @@ if (!pSimulation2->GetScriptInterface().GetProperty(val, #prop, out))\ LOGWARNING("CMapReader::ParseCamera() failed to get '%s' property", #prop); - JS::RootedValue cameraObj(cx); + JS::RootedValue cameraObj(rq.cx); GET_CAMERA_PROPERTY(m_MapData, Camera, &cameraObj) if (!cameraObj.isUndefined()) Index: ps/trunk/source/graphics/scripting/JSInterface_GameView.cpp =================================================================== --- ps/trunk/source/graphics/scripting/JSInterface_GameView.cpp +++ ps/trunk/source/graphics/scripting/JSInterface_GameView.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -70,14 +70,13 @@ JS::Value JSI_GameView::GetCameraPivot(ScriptInterface::CxPrivate* pCxPrivate) { - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(pCxPrivate); CVector3D pivot(-1, -1, -1); if (g_Game && g_Game->GetView()) pivot = g_Game->GetView()->GetCameraPivot(); - JS::RootedValue pivotValue(cx); - ScriptInterface::CreateObject(cx, &pivotValue, "x", pivot.X, "z", pivot.Z); + JS::RootedValue pivotValue(rq.cx); + ScriptInterface::CreateObject(rq, &pivotValue, "x", pivot.X, "z", pivot.Z); return pivotValue; } Index: ps/trunk/source/gui/CGUI.cpp =================================================================== --- ps/trunk/source/gui/CGUI.cpp +++ ps/trunk/source/gui/CGUI.cpp @@ -97,11 +97,10 @@ { ret = IN_HANDLED; - JSContext* cx = m_ScriptInterface->GetContext(); - JSAutoRequest rq(cx); - JS::RootedObject globalObj(cx, &GetGlobalObject().toObject()); - JS::RootedValue result(cx); - JS_CallFunctionValue(cx, globalObj, m_GlobalHotkeys[hotkey][eventName], JS::HandleValueArray::empty(), &result); + ScriptInterface::Request rq(*m_ScriptInterface); + JS::RootedObject globalObj(rq.cx, &GetGlobalObject().toObject()); + JS::RootedValue result(rq.cx); + JS_CallFunctionValue(rq.cx, globalObj, m_GlobalHotkeys[hotkey][eventName], JS::HandleValueArray::empty(), &result); } std::map >::iterator it = m_HotkeyObjects.find(hotkey); @@ -407,30 +406,29 @@ void CGUI::SetGlobalHotkey(const CStr& hotkeyTag, const CStr& eventName, JS::HandleValue function) { - JSContext* cx = m_ScriptInterface->GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(*m_ScriptInterface); if (hotkeyTag.empty()) { - JS_ReportError(cx, "Cannot assign a function to an empty hotkey identifier!"); + JS_ReportError(rq.cx, "Cannot assign a function to an empty hotkey identifier!"); return; } // Only support "Press", "Keydown" and "Release" events. if (eventName != EventNamePress && eventName != EventNameKeyDown && eventName != EventNameRelease) { - JS_ReportError(cx, "Cannot assign a function to an unsupported event!"); + JS_ReportError(rq.cx, "Cannot assign a function to an unsupported event!"); return; } - if (!function.isObject() || !JS_ObjectIsFunction(cx, &function.toObject())) + if (!function.isObject() || !JS_ObjectIsFunction(rq.cx, &function.toObject())) { - JS_ReportError(cx, "Cannot assign non-function value to global hotkey '%s'", hotkeyTag.c_str()); + JS_ReportError(rq.cx, "Cannot assign non-function value to global hotkey '%s'", hotkeyTag.c_str()); return; } UnsetGlobalHotkey(hotkeyTag, eventName); - m_GlobalHotkeys[hotkeyTag][eventName].init(cx, function); + m_GlobalHotkeys[hotkeyTag][eventName].init(rq.cx, function); } void CGUI::UnsetGlobalHotkey(const CStr& hotkeyTag, const CStr& eventName) Index: ps/trunk/source/gui/CGUISetting.h =================================================================== --- ps/trunk/source/gui/CGUISetting.h +++ ps/trunk/source/gui/CGUISetting.h @@ -41,12 +41,12 @@ /** * Parses the given JS::Value using ScriptInterface::FromJSVal and assigns it to the setting data. */ - virtual bool FromJSVal(JSContext* cx, JS::HandleValue Value, const bool SendMessage) = 0; + virtual bool FromJSVal(const ScriptInterface::Request& rq, JS::HandleValue Value, const bool SendMessage) = 0; /** * Converts the setting data to a JS::Value using ScriptInterface::ToJSVal. */ - virtual void ToJSVal(JSContext* cx, JS::MutableHandleValue Value) = 0; + virtual void ToJSVal(const ScriptInterface::Request& rq, JS::MutableHandleValue Value) = 0; }; template @@ -65,12 +65,12 @@ /** * Parses the given JS::Value using ScriptInterface::FromJSVal and assigns it to the setting data. */ - bool FromJSVal(JSContext* cx, JS::HandleValue Value, const bool SendMessage) override; + bool FromJSVal(const ScriptInterface::Request& rq, JS::HandleValue Value, const bool SendMessage) override; /** * Converts the setting data to a JS::Value using ScriptInterface::ToJSVal. */ - void ToJSVal(JSContext* cx, JS::MutableHandleValue Value) override; + void ToJSVal(const ScriptInterface::Request& rq, JS::MutableHandleValue Value) override; /** * These members are public because they are either unmodifiable or free to be modified. Index: ps/trunk/source/gui/CGUISetting.cpp =================================================================== --- ps/trunk/source/gui/CGUISetting.cpp +++ ps/trunk/source/gui/CGUISetting.cpp @@ -41,23 +41,22 @@ }; template<> -bool CGUISetting::FromJSVal(JSContext* cx, JS::HandleValue Value, const bool SendMessage) +bool CGUISetting::FromJSVal(const ScriptInterface::Request& rq, JS::HandleValue Value, const bool SendMessage) { CGUIColor settingValue; if (Value.isString()) { CStr name; - if (!ScriptInterface::FromJSVal(cx, Value, name)) + if (!ScriptInterface::FromJSVal(rq, Value, name)) return false; if (!settingValue.ParseString(m_pObject.GetGUI(), name)) { - JSAutoRequest rq(cx); - JS_ReportError(cx, "Invalid color '%s'", name.c_str()); + JS_ReportError(rq.cx, "Invalid color '%s'", name.c_str()); return false; } } - else if (!ScriptInterface::FromJSVal(cx, Value, settingValue)) + else if (!ScriptInterface::FromJSVal(rq, Value, settingValue)) return false; m_pObject.SetSetting(m_Name, settingValue, SendMessage); @@ -65,10 +64,10 @@ }; template -bool CGUISetting::FromJSVal(JSContext* cx, JS::HandleValue Value, const bool SendMessage) +bool CGUISetting::FromJSVal(const ScriptInterface::Request& rq, JS::HandleValue Value, const bool SendMessage) { T settingValue; - if (!ScriptInterface::FromJSVal(cx, Value, settingValue)) + if (!ScriptInterface::FromJSVal(rq, Value, settingValue)) return false; m_pObject.SetSetting(m_Name, settingValue, SendMessage); @@ -76,9 +75,9 @@ }; template -void CGUISetting::ToJSVal(JSContext* cx, JS::MutableHandleValue Value) +void CGUISetting::ToJSVal(const ScriptInterface::Request& rq, JS::MutableHandleValue Value) { - ScriptInterface::ToJSVal(cx, Value, m_pSetting); + ScriptInterface::ToJSVal(rq, Value, m_pSetting); }; #define TYPE(T) \ Index: ps/trunk/source/gui/GUIManager.cpp =================================================================== --- ps/trunk/source/gui/GUIManager.cpp +++ ps/trunk/source/gui/GUIManager.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -133,11 +133,10 @@ if (gui) { shared_ptr scriptInterface = gui->GetScriptInterface(); - JSContext* cx = scriptInterface->GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(*scriptInterface); - JS::RootedValue global(cx, scriptInterface->GetGlobalObject()); - JS::RootedValue hotloadDataVal(cx); + JS::RootedValue global(rq.cx, scriptInterface->GetGlobalObject()); + JS::RootedValue hotloadDataVal(rq.cx); scriptInterface->CallFunction(global, "getHotloadData", &hotloadDataVal); hotloadData = scriptInterface->WriteStructuredClone(hotloadDataVal); } @@ -200,12 +199,11 @@ gui->LoadedXmlFiles(); shared_ptr scriptInterface = gui->GetScriptInterface(); - JSContext* cx = scriptInterface->GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(*scriptInterface); - JS::RootedValue initDataVal(cx); - JS::RootedValue hotloadDataVal(cx); - JS::RootedValue global(cx, scriptInterface->GetGlobalObject()); + JS::RootedValue initDataVal(rq.cx); + JS::RootedValue hotloadDataVal(rq.cx); + JS::RootedValue global(rq.cx, scriptInterface->GetGlobalObject()); if (initData) scriptInterface->ReadStructuredClone(initData, &initDataVal); @@ -226,8 +224,9 @@ return; } - // Does not require JSAutoRequest - if (!JS_ObjectIsFunction(scriptInterface.GetContext(), &callbackFunc.toObject())) + ScriptInterface::Request rq(scriptInterface); + + if (!JS_ObjectIsFunction(rq.cx, &callbackFunc.toObject())) { LOGERROR("Given callback handler is not a function!"); return; @@ -242,26 +241,25 @@ return; shared_ptr scriptInterface = gui->GetScriptInterface(); - JSContext* cx = scriptInterface->GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(*scriptInterface); - JS::RootedObject globalObj(cx, &scriptInterface->GetGlobalObject().toObject()); + JS::RootedObject globalObj(rq.cx, &scriptInterface->GetGlobalObject().toObject()); - JS::RootedValue funcVal(cx, *callbackFunction); + JS::RootedValue funcVal(rq.cx, *callbackFunction); // Delete the callback function, so that it is not called again callbackFunction.reset(); - JS::RootedValue argVal(cx); + JS::RootedValue argVal(rq.cx); if (args) scriptInterface->ReadStructuredClone(args, &argVal); - JS::AutoValueVector paramData(cx); + JS::AutoValueVector paramData(rq.cx); paramData.append(argVal); - JS::RootedValue result(cx); + JS::RootedValue result(rq.cx); - JS_CallFunctionValue(cx, globalObj, funcVal, paramData, &result); + JS_CallFunctionValue(rq.cx, globalObj, funcVal, paramData, &result); } Status CGUIManager::ReloadChangedFile(const VfsPath& path) @@ -299,9 +297,9 @@ { PROFILE("handleInputBeforeGui"); - JSContext* cx = top()->GetScriptInterface()->GetContext(); - JSAutoRequest rq(cx); - JS::RootedValue global(cx, top()->GetGlobalObject()); + ScriptInterface::Request rq(*top()->GetScriptInterface()); + + JS::RootedValue global(rq.cx, top()->GetGlobalObject()); if (top()->GetScriptInterface()->CallFunction(global, "handleInputBeforeGui", handled, *ev, top()->FindObjectUnderMouse())) if (handled) return IN_HANDLED; @@ -316,9 +314,8 @@ { // We can't take the following lines out of this scope because top() may be another gui page than it was when calling handleInputBeforeGui! - JSContext* cx = top()->GetScriptInterface()->GetContext(); - JSAutoRequest rq(cx); - JS::RootedValue global(cx, top()->GetGlobalObject()); + ScriptInterface::Request rq(*top()->GetScriptInterface()); + JS::RootedValue global(rq.cx, top()->GetGlobalObject()); PROFILE("handleInputAfterGui"); if (top()->GetScriptInterface()->CallFunction(global, "handleInputAfterGui", handled, *ev)) Index: ps/trunk/source/gui/ObjectBases/IGUIObject.cpp =================================================================== --- ps/trunk/source/gui/ObjectBases/IGUIObject.cpp +++ ps/trunk/source/gui/ObjectBases/IGUIObject.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -299,10 +299,9 @@ void IGUIObject::RegisterScriptHandler(const CStr& eventName, const CStr& Code, CGUI& pGUI) { - JSContext* cx = pGUI.GetScriptInterface()->GetContext(); - JSAutoRequest rq(cx); - JS::RootedValue globalVal(cx, pGUI.GetGlobalObject()); - JS::RootedObject globalObj(cx, &globalVal.toObject()); + ScriptInterface::Request rq(pGUI.GetScriptInterface()); + JS::RootedValue globalVal(rq.cx, pGUI.GetGlobalObject()); + JS::RootedObject globalObj(rq.cx, &globalVal.toObject()); const int paramCount = 1; const char* paramNames[paramCount] = { "mouse" }; @@ -315,19 +314,19 @@ char buf[64]; sprintf_s(buf, ARRAY_SIZE(buf), "__eventhandler%d (%s)", x++, eventName.c_str()); - JS::CompileOptions options(cx); + JS::CompileOptions options(rq.cx); options.setFileAndLine(CodeName.c_str(), 0); options.setIsRunOnce(false); - JS::RootedFunction func(cx); - JS::AutoObjectVector emptyScopeChain(cx); - if (!JS::CompileFunction(cx, emptyScopeChain, options, buf, paramCount, paramNames, Code.c_str(), Code.length(), &func)) + 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)) { LOGERROR("RegisterScriptHandler: Failed to compile the script for %s", eventName.c_str()); return; } - JS::RootedObject funcObj(cx, JS_GetFunctionObject(func)); + JS::RootedObject funcObj(rq.cx, JS_GetFunctionObject(func)); SetScriptHandler(eventName, funcObj); } @@ -375,21 +374,20 @@ SGUIMessage msg(type); HandleMessage(msg); - JSContext* cx = m_pGUI.GetScriptInterface()->GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(m_pGUI.GetScriptInterface()); // Set up the 'mouse' parameter - JS::RootedValue mouse(cx); + JS::RootedValue mouse(rq.cx); const CPos& mousePos = m_pGUI.GetMousePos(); ScriptInterface::CreateObject( - cx, + rq, &mouse, "x", mousePos.x, "y", mousePos.y, "buttons", m_pGUI.GetMouseButtons()); - JS::AutoValueVector paramData(cx); + JS::AutoValueVector paramData(rq.cx); paramData.append(mouse); ScriptEvent(eventName, paramData); @@ -406,9 +404,8 @@ if (m_ScriptHandlers.find(eventName) == m_ScriptHandlers.end()) return false; - JSContext* cx = m_pGUI.GetScriptInterface()->GetContext(); - JSAutoRequest rq(cx); - JS::AutoValueVector paramData(cx); + ScriptInterface::Request rq(m_pGUI.GetScriptInterface()); + JS::AutoValueVector paramData(rq.cx); return ScriptEventWithReturn(eventName, paramData); } @@ -423,13 +420,12 @@ if (it == m_ScriptHandlers.end()) return false; - JSContext* cx = m_pGUI.GetScriptInterface()->GetContext(); - JSAutoRequest rq(cx); - JS::RootedObject obj(cx, GetJSObject()); - JS::RootedValue handlerVal(cx, JS::ObjectValue(*it->second)); - JS::RootedValue result(cx); + ScriptInterface::Request rq(m_pGUI.GetScriptInterface()); + JS::RootedObject obj(rq.cx, GetJSObject()); + JS::RootedValue handlerVal(rq.cx, JS::ObjectValue(*it->second)); + JS::RootedValue result(rq.cx); - if (!JS_CallFunctionValue(cx, obj, handlerVal, paramData, &result)) + if (!JS_CallFunctionValue(rq.cx, obj, handlerVal, paramData, &result)) { LOGERROR("Errors executing script event \"%s\"", eventName.c_str()); return false; @@ -439,10 +435,9 @@ void IGUIObject::CreateJSObject() { - JSContext* cx = m_pGUI.GetScriptInterface()->GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(m_pGUI.GetScriptInterface()); - m_JSObject.init(cx, m_pGUI.GetScriptInterface()->CreateCustomObject("GUIObject")); + m_JSObject.init(rq.cx, m_pGUI.GetScriptInterface()->CreateCustomObject("GUIObject")); JS_SetPrivate(m_JSObject.get(), this); RegisterScriptFunctions(); Index: ps/trunk/source/gui/ObjectTypes/CMiniMap.cpp =================================================================== --- ps/trunk/source/gui/ObjectTypes/CMiniMap.cpp +++ ps/trunk/source/gui/ObjectTypes/CMiniMap.cpp @@ -238,19 +238,18 @@ bool CMiniMap::FireWorldClickEvent(int button, int UNUSED(clicks)) { - JSContext* cx = g_GUI->GetActiveGUI()->GetScriptInterface()->GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(g_GUI->GetActiveGUI()->GetScriptInterface()); float x, z; GetMouseWorldCoordinates(x, z); - JS::RootedValue coords(cx); - ScriptInterface::CreateObject(cx, &coords, "x", x, "z", z); + JS::RootedValue coords(rq.cx); + ScriptInterface::CreateObject(rq, &coords, "x", x, "z", z); - JS::RootedValue buttonJs(cx); - ScriptInterface::ToJSVal(cx, &buttonJs, button); + JS::RootedValue buttonJs(rq.cx); + ScriptInterface::ToJSVal(rq, &buttonJs, button); - JS::AutoValueVector paramData(cx); + JS::AutoValueVector paramData(rq.cx); paramData.append(coords); paramData.append(buttonJs); Index: ps/trunk/source/gui/ObjectTypes/CText.cpp =================================================================== --- ps/trunk/source/gui/ObjectTypes/CText.cpp +++ ps/trunk/source/gui/ObjectTypes/CText.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -254,9 +254,8 @@ void CText::RegisterScriptFunctions() { - JSContext* cx = m_pGUI.GetScriptInterface()->GetContext(); - JSAutoRequest rq(cx); - JS_DefineFunctions(cx, m_JSObject, CText::JSI_methods); + ScriptInterface::Request rq(m_pGUI.GetScriptInterface()); + JS_DefineFunctions(rq.cx, m_JSObject, CText::JSI_methods); } JSFunctionSpec CText::JSI_methods[] = @@ -267,18 +266,18 @@ bool CText::GetTextSize(JSContext* cx, uint argc, JS::Value* vp) { - // No JSAutoRequest needed for these calls JS::CallArgs args = JS::CallArgsFromVp(argc, vp); - CText* thisObj = ScriptInterface::GetPrivate(cx, args, &JSI_IGUIObject::JSI_class); + + ScriptInterface::Request rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); + CText* thisObj = ScriptInterface::GetPrivate(rq, args, &JSI_IGUIObject::JSI_class); if (!thisObj) { - JSAutoRequest rq(cx); JS_ReportError(cx, "This is not a CText object!"); return false; } thisObj->UpdateText(); - ScriptInterface::ToJSVal(cx, args.rval(), thisObj->m_GeneratedTexts[0].GetSize()); + ScriptInterface::ToJSVal(rq, args.rval(), thisObj->m_GeneratedTexts[0].GetSize()); return true; } Index: ps/trunk/source/gui/Scripting/GuiScriptConversions.cpp =================================================================== --- ps/trunk/source/gui/Scripting/GuiScriptConversions.cpp +++ ps/trunk/source/gui/Scripting/GuiScriptConversions.cpp @@ -29,13 +29,12 @@ #include -#define SET(obj, name, value) STMT(JS::RootedValue v_(cx); AssignOrToJSVal(cx, &v_, (value)); JS_SetProperty(cx, obj, (name), v_)) +#define SET(obj, name, value) STMT(JS::RootedValue v_(rq.cx); AssignOrToJSVal(rq, &v_, (value)); JS_SetProperty(rq.cx, obj, (name), v_)) // ignore JS_SetProperty return value, because errors should be impossible // and we can't do anything useful in the case of errors anyway -template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, SDL_Event_ const& val) +template<> void ScriptInterface::ToJSVal(const Request& rq, JS::MutableHandleValue ret, SDL_Event_ const& val) { - JSAutoRequest rq(cx); const char* typeName; switch (val.ev.type) @@ -52,7 +51,7 @@ default: typeName = "(unknown)"; break; } - JS::RootedObject obj(cx, JS_NewPlainObject(cx)); + JS::RootedObject obj(rq.cx, JS_NewPlainObject(rq.cx)); if (!obj) { ret.setUndefined(); @@ -69,14 +68,14 @@ // SET(obj, "which", (int)val.ev.key.which); // (not in wsdl.h) // SET(obj, "state", (int)val.ev.key.state); // (not in wsdl.h) - JS::RootedObject keysym(cx, JS_NewPlainObject(cx)); + JS::RootedObject keysym(rq.cx, JS_NewPlainObject(rq.cx)); if (!keysym) { ret.setUndefined(); return; } - JS::RootedValue keysymVal(cx, JS::ObjectValue(*keysym)); - JS_SetProperty(cx, obj, "keysym", keysymVal); + JS::RootedValue keysymVal(rq.cx, JS::ObjectValue(*keysym)); + JS_SetProperty(rq.cx, obj, "keysym", keysymVal); // SET(keysym, "scancode", (int)val.ev.key.keysym.scancode); // (not in wsdl.h) SET(keysym, "sym", (int)val.ev.key.keysym.sym); @@ -121,7 +120,7 @@ ret.setObject(*obj); } -template<> void ScriptInterface::ToJSVal(JSContext* UNUSED(cx), JS::MutableHandleValue ret, IGUIObject* const& val) +template<> void ScriptInterface::ToJSVal(const Request& UNUSED(rq), JS::MutableHandleValue ret, IGUIObject* const& val) { if (val == nullptr) ret.setNull(); @@ -129,15 +128,15 @@ ret.setObject(*val->GetJSObject()); } -template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const CGUIString& val) +template<> void ScriptInterface::ToJSVal(const Request& rq, JS::MutableHandleValue ret, const CGUIString& val) { - ScriptInterface::ToJSVal(cx, ret, val.GetOriginalString()); + ScriptInterface::ToJSVal(rq, ret, val.GetOriginalString()); } -template<> bool ScriptInterface::FromJSVal(JSContext* cx, JS::HandleValue v, CGUIString& out) +template<> bool ScriptInterface::FromJSVal(const Request& rq, JS::HandleValue v, CGUIString& out) { std::wstring val; - if (!FromJSVal(cx, v, val)) + if (!FromJSVal(rq, v, val)) return false; out.SetValue(val); return true; @@ -147,82 +146,76 @@ JSVAL_VECTOR(std::vector) JSVAL_VECTOR(CGUIString) -template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const CGUIColor& val) +template<> void ScriptInterface::ToJSVal(const Request& rq, JS::MutableHandleValue ret, const CGUIColor& val) { - ToJSVal(cx, ret, val); + ToJSVal(rq, ret, val); } /** * The color depends on the predefined color database stored in the current GUI page. */ -template<> bool ScriptInterface::FromJSVal(JSContext* cx, JS::HandleValue v, CGUIColor& out) = delete; +template<> bool ScriptInterface::FromJSVal(const Request& rq, JS::HandleValue v, CGUIColor& out) = delete; -template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const CSize& val) +template<> void ScriptInterface::ToJSVal(const Request& rq, JS::MutableHandleValue ret, const CSize& val) { - CreateObject(cx, ret, "width", val.cx, "height", val.cy); + CreateObject(rq, ret, "width", val.cx, "height", val.cy); } -template<> bool ScriptInterface::FromJSVal(JSContext* cx, JS::HandleValue v, CSize& out) +template<> bool ScriptInterface::FromJSVal(const Request& rq, JS::HandleValue v, CSize& out) { if (!v.isObject()) { - JSAutoRequest rq(cx); - JS_ReportError(cx, "CSize value must be an object!"); + JS_ReportError(rq.cx, "CSize value must be an object!"); return false; } - if (!FromJSProperty(cx, v, "width", out.cx)) + if (!FromJSProperty(rq, v, "width", out.cx)) { - JSAutoRequest rq(cx); - JS_ReportError(cx, "Failed to get CSize.cx property"); + JS_ReportError(rq.cx, "Failed to get CSize.cx property"); return false; } - if (!FromJSProperty(cx, v, "height", out.cy)) + if (!FromJSProperty(rq, v, "height", out.cy)) { - JSAutoRequest rq(cx); - JS_ReportError(cx, "Failed to get CSize.cy property"); + JS_ReportError(rq.cx, "Failed to get CSize.cy property"); return false; } return true; } -template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const CPos& val) +template<> void ScriptInterface::ToJSVal(const Request& rq, JS::MutableHandleValue ret, const CPos& val) { - CreateObject(cx, ret, "x", val.x, "y", val.y); + CreateObject(rq, ret, "x", val.x, "y", val.y); } -template<> bool ScriptInterface::FromJSVal(JSContext* cx, JS::HandleValue v, CPos& out) +template<> bool ScriptInterface::FromJSVal(const Request& rq, JS::HandleValue v, CPos& out) { if (!v.isObject()) { - JSAutoRequest rq(cx); - JS_ReportError(cx, "CPos value must be an object!"); + JS_ReportError(rq.cx, "CPos value must be an object!"); return false; } - if (!FromJSProperty(cx, v, "x", out.x)) + if (!FromJSProperty(rq, v, "x", out.x)) { - JSAutoRequest rq(cx); - JS_ReportError(cx, "Failed to get CPos.x property"); + JS_ReportError(rq.cx, "Failed to get CPos.x property"); return false; } - if (!FromJSProperty(cx, v, "y", out.y)) + if (!FromJSProperty(rq, v, "y", out.y)) { - JSAutoRequest rq(cx); - JS_ReportError(cx, "Failed to get CPos.y property"); + JS_ReportError(rq.cx, "Failed to get CPos.y property"); return false; } return true; } -template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const CRect& val) +template<> void ScriptInterface::ToJSVal(const Request& rq, JS::MutableHandleValue ret, const CRect& val) { CreateObject( - cx, + rq, ret, "left", val.left, "right", val.right, @@ -230,37 +223,37 @@ "bottom", val.bottom); } -template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const CGUISize& val) +template<> void ScriptInterface::ToJSVal(const Request& rq, JS::MutableHandleValue ret, const CGUISize& val) { - val.ToJSVal(cx, ret); + val.ToJSVal(rq, ret); } -template<> bool ScriptInterface::FromJSVal(JSContext* cx, JS::HandleValue v, CGUISize& out) +template<> bool ScriptInterface::FromJSVal(const Request& rq, JS::HandleValue v, CGUISize& out) { - return out.FromJSVal(cx, v); + return out.FromJSVal(rq, v); } -template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const CGUIList& val) +template<> void ScriptInterface::ToJSVal(const Request& rq, JS::MutableHandleValue ret, const CGUIList& val) { - ToJSVal(cx, ret, val.m_Items); + ToJSVal(rq, ret, val.m_Items); } -template<> bool ScriptInterface::FromJSVal(JSContext* cx, JS::HandleValue v, CGUIList& out) +template<> bool ScriptInterface::FromJSVal(const Request& rq, JS::HandleValue v, CGUIList& out) { - return FromJSVal(cx, v, out.m_Items); + return FromJSVal(rq, v, out.m_Items); } -template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const CGUISeries& val) +template<> void ScriptInterface::ToJSVal(const Request& rq, JS::MutableHandleValue ret, const CGUISeries& val) { - ToJSVal(cx, ret, val.m_Series); + ToJSVal(rq, ret, val.m_Series); } -template<> bool ScriptInterface::FromJSVal(JSContext* cx, JS::HandleValue v, CGUISeries& out) +template<> bool ScriptInterface::FromJSVal(const Request& rq, JS::HandleValue v, CGUISeries& out) { - return FromJSVal(cx, v, out.m_Series); + return FromJSVal(rq, v, out.m_Series); } -template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const EVAlign& val) +template<> void ScriptInterface::ToJSVal(const Request& rq, JS::MutableHandleValue ret, const EVAlign& val) { std::string word; switch (val) @@ -279,17 +272,16 @@ default: word = "error"; - JSAutoRequest rq(cx); - JS_ReportError(cx, "Invalid EVAlign"); + JS_ReportError(rq.cx, "Invalid EVAlign"); break; } - ToJSVal(cx, ret, word); + ToJSVal(rq, ret, word); } -template<> bool ScriptInterface::FromJSVal(JSContext* cx, JS::HandleValue v, EVAlign& out) +template<> bool ScriptInterface::FromJSVal(const Request& rq, JS::HandleValue v, EVAlign& out) { std::string word; - FromJSVal(cx, v, word); + FromJSVal(rq, v, word); if (word == "top") out = EVAlign_Top; @@ -300,14 +292,13 @@ else { out = EVAlign_Top; - JSAutoRequest rq(cx); - JS_ReportError(cx, "Invalid alignment (should be 'left', 'right' or 'center')"); + JS_ReportError(rq.cx, "Invalid alignment (should be 'left', 'right' or 'center')"); return false; } return true; } -template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const EAlign& val) +template<> void ScriptInterface::ToJSVal(const Request& rq, JS::MutableHandleValue ret, const EAlign& val) { std::string word; switch (val) @@ -323,17 +314,16 @@ break; default: word = "error"; - JSAutoRequest rq(cx); - JS_ReportError(cx, "Invalid alignment (should be 'left', 'right' or 'center')"); + JS_ReportError(rq.cx, "Invalid alignment (should be 'left', 'right' or 'center')"); break; } - ToJSVal(cx, ret, word); + ToJSVal(rq, ret, word); } -template<> bool ScriptInterface::FromJSVal(JSContext* cx, JS::HandleValue v, EAlign& out) +template<> bool ScriptInterface::FromJSVal(const Request& rq, JS::HandleValue v, EAlign& out) { std::string word; - FromJSVal(cx, v, word); + FromJSVal(rq, v, word); if (word == "left") out = EAlign_Left; @@ -344,22 +334,21 @@ else { out = EAlign_Left; - JSAutoRequest rq(cx); - JS_ReportError(cx, "Invalid alignment (should be 'left', 'right' or 'center')"); + JS_ReportError(rq.cx, "Invalid alignment (should be 'left', 'right' or 'center')"); return false; } return true; } -template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const CGUISpriteInstance& val) +template<> void ScriptInterface::ToJSVal(const Request& rq, JS::MutableHandleValue ret, const CGUISpriteInstance& val) { - ToJSVal(cx, ret, val.GetName()); + ToJSVal(rq, ret, val.GetName()); } -template<> bool ScriptInterface::FromJSVal(JSContext* cx, JS::HandleValue v, CGUISpriteInstance& out) +template<> bool ScriptInterface::FromJSVal(const Request& rq, JS::HandleValue v, CGUISpriteInstance& out) { std::string name; - if (!FromJSVal(cx, v, name)) + if (!FromJSVal(rq, v, name)) return false; out.SetName(name); Index: ps/trunk/source/gui/Scripting/JSInterface_GUIManager.cpp =================================================================== --- ps/trunk/source/gui/Scripting/JSInterface_GUIManager.cpp +++ ps/trunk/source/gui/Scripting/JSInterface_GUIManager.cpp @@ -41,9 +41,8 @@ { if (g_GUI->GetPageCount() < 2) { - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); - JS_ReportError(cx, "Can't pop GUI pages when less than two pages are opened!"); + ScriptInterface::Request rq(pCxPrivate); + JS_ReportError(rq.cx, "Can't pop GUI pages when less than two pages are opened!"); return; } Index: ps/trunk/source/gui/Scripting/JSInterface_GUISize.cpp =================================================================== --- ps/trunk/source/gui/Scripting/JSInterface_GUISize.cpp +++ ps/trunk/source/gui/Scripting/JSInterface_GUISize.cpp @@ -43,45 +43,46 @@ bool JSI_GUISize::construct(JSContext* cx, uint argc, JS::Value* vp) { - JSAutoRequest rq(cx); JS::CallArgs args = JS::CallArgsFromVp(argc, vp); ScriptInterface* pScriptInterface = ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface; - JS::RootedObject obj(cx, pScriptInterface->CreateCustomObject("GUISize")); + ScriptInterface::Request rq(*pScriptInterface); + + JS::RootedObject obj(rq.cx, pScriptInterface->CreateCustomObject("GUISize")); if (args.length() == 8) { - JS_SetProperty(cx, obj, "left", args[0]); - JS_SetProperty(cx, obj, "top", args[1]); - JS_SetProperty(cx, obj, "right", args[2]); - JS_SetProperty(cx, obj, "bottom", args[3]); - JS_SetProperty(cx, obj, "rleft", args[4]); - JS_SetProperty(cx, obj, "rtop", args[5]); - JS_SetProperty(cx, obj, "rright", args[6]); - JS_SetProperty(cx, obj, "rbottom", args[7]); + JS_SetProperty(rq.cx, obj, "left", args[0]); + JS_SetProperty(rq.cx, obj, "top", args[1]); + JS_SetProperty(rq.cx, obj, "right", args[2]); + JS_SetProperty(rq.cx, obj, "bottom", args[3]); + JS_SetProperty(rq.cx, obj, "rleft", args[4]); + JS_SetProperty(rq.cx, obj, "rtop", args[5]); + JS_SetProperty(rq.cx, obj, "rright", args[6]); + JS_SetProperty(rq.cx, obj, "rbottom", args[7]); } else if (args.length() == 4) { - JS::RootedValue zero(cx, JS::NumberValue(0)); - JS_SetProperty(cx, obj, "left", args[0]); - JS_SetProperty(cx, obj, "top", args[1]); - JS_SetProperty(cx, obj, "right", args[2]); - JS_SetProperty(cx, obj, "bottom", args[3]); - JS_SetProperty(cx, obj, "rleft", zero); - JS_SetProperty(cx, obj, "rtop", zero); - JS_SetProperty(cx, obj, "rright", zero); - JS_SetProperty(cx, obj, "rbottom", zero); + JS::RootedValue zero(rq.cx, JS::NumberValue(0)); + JS_SetProperty(rq.cx, obj, "left", args[0]); + JS_SetProperty(rq.cx, obj, "top", args[1]); + JS_SetProperty(rq.cx, obj, "right", args[2]); + JS_SetProperty(rq.cx, obj, "bottom", args[3]); + JS_SetProperty(rq.cx, obj, "rleft", zero); + JS_SetProperty(rq.cx, obj, "rtop", zero); + JS_SetProperty(rq.cx, obj, "rright", zero); + JS_SetProperty(rq.cx, obj, "rbottom", zero); } else { - JS::RootedValue zero(cx, JS::NumberValue(0)); - JS_SetProperty(cx, obj, "left", zero); - JS_SetProperty(cx, obj, "top", zero); - JS_SetProperty(cx, obj, "right", zero); - JS_SetProperty(cx, obj, "bottom", zero); - JS_SetProperty(cx, obj, "rleft", zero); - JS_SetProperty(cx, obj, "rtop", zero); - JS_SetProperty(cx, obj, "rright", zero); - JS_SetProperty(cx, obj, "rbottom", zero); + JS::RootedValue zero(rq.cx, JS::NumberValue(0)); + JS_SetProperty(rq.cx, obj, "left", zero); + JS_SetProperty(rq.cx, obj, "top", zero); + JS_SetProperty(rq.cx, obj, "right", zero); + JS_SetProperty(rq.cx, obj, "bottom", zero); + JS_SetProperty(rq.cx, obj, "rleft", zero); + JS_SetProperty(rq.cx, obj, "rtop", zero); + JS_SetProperty(rq.cx, obj, "rright", zero); + JS_SetProperty(rq.cx, obj, "rbottom", zero); } args.rval().setObject(*obj); @@ -99,11 +100,11 @@ bool JSI_GUISize::toString(JSContext* cx, uint argc, JS::Value* vp) { - // JSAutoRequest not needed for the calls below JS::CallArgs args = JS::CallArgsFromVp(argc, vp); CStr buffer; ScriptInterface* pScriptInterface = ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface; + ScriptInterface::Request rq(*pScriptInterface); double val, valr; #define SIDE(side) \ @@ -120,6 +121,6 @@ SIDE(bottom); #undef SIDE - ScriptInterface::ToJSVal(cx, args.rval(), buffer); + ScriptInterface::ToJSVal(rq, args.rval(), buffer); return true; } Index: ps/trunk/source/gui/Scripting/JSInterface_IGUIObject.cpp =================================================================== --- ps/trunk/source/gui/Scripting/JSInterface_IGUIObject.cpp +++ ps/trunk/source/gui/Scripting/JSInterface_IGUIObject.cpp @@ -52,19 +52,19 @@ bool JSI_IGUIObject::getProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::MutableHandleValue vp) { - JSAutoRequest rq(cx); ScriptInterface* pScriptInterface = ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface; + ScriptInterface::Request rq(*pScriptInterface); - IGUIObject* e = ScriptInterface::GetPrivate(cx, obj, &JSI_IGUIObject::JSI_class); + IGUIObject* e = ScriptInterface::GetPrivate(rq, obj, &JSI_IGUIObject::JSI_class); if (!e) return false; - JS::RootedValue idval(cx); - if (!JS_IdToValue(cx, id, &idval)) + JS::RootedValue idval(rq.cx); + if (!JS_IdToValue(rq.cx, id, &idval)) return false; std::string propName; - if (!ScriptInterface::FromJSVal(cx, idval, propName)) + if (!ScriptInterface::FromJSVal(rq, idval, propName)) return false; // Skip registered functions and inherited properties @@ -105,7 +105,7 @@ } else if (propName == "children") { - ScriptInterface::CreateArray(cx, vp); + ScriptInterface::CreateArray(rq, vp); for (size_t i = 0; i < e->m_Children.size(); ++i) pScriptInterface->SetPropertyInt(vp, i, e->m_Children[i]); @@ -114,12 +114,12 @@ } else if (propName == "name") { - ScriptInterface::ToJSVal(cx, vp, e->GetName()); + ScriptInterface::ToJSVal(rq, vp, e->GetName()); return true; } else if (e->SettingExists(propName)) { - e->m_Settings[propName]->ToJSVal(cx, vp); + e->m_Settings[propName]->ToJSVal(rq, vp); return true; } @@ -129,36 +129,37 @@ bool JSI_IGUIObject::setProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::MutableHandleValue vp, JS::ObjectOpResult& result) { - IGUIObject* e = ScriptInterface::GetPrivate(cx, obj, &JSI_IGUIObject::JSI_class); + ScriptInterface::Request rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); + + IGUIObject* e = ScriptInterface::GetPrivate(rq, obj, &JSI_IGUIObject::JSI_class); if (!e) return result.fail(JSMSG_NOT_NONNULL_OBJECT); - JSAutoRequest rq(cx); - JS::RootedValue idval(cx); - if (!JS_IdToValue(cx, id, &idval)) + JS::RootedValue idval(rq.cx); + if (!JS_IdToValue(rq.cx, id, &idval)) return result.fail(JSMSG_NOT_NONNULL_OBJECT); std::string propName; - if (!ScriptInterface::FromJSVal(cx, idval, propName)) + if (!ScriptInterface::FromJSVal(rq, idval, propName)) return result.fail(JSMSG_UNDEFINED_PROP); if (propName == "name") { std::string value; - if (!ScriptInterface::FromJSVal(cx, vp, value)) + if (!ScriptInterface::FromJSVal(rq, vp, value)) return result.fail(JSMSG_UNDEFINED_PROP); e->SetName(value); return result.succeed(); } - JS::RootedObject vpObj(cx); + JS::RootedObject vpObj(rq.cx); if (vp.isObject()) vpObj = &vp.toObject(); // Use onWhatever to set event handlers if (propName.substr(0, 2) == "on") { - if (vp.isPrimitive() || vp.isNull() || !JS_ObjectIsFunction(cx, &vp.toObject())) + if (vp.isPrimitive() || vp.isNull() || !JS_ObjectIsFunction(rq.cx, &vp.toObject())) { LOGERROR("on- event-handlers must be functions"); return result.fail(JSMSG_NOT_FUNCTION); @@ -171,7 +172,7 @@ } if (e->SettingExists(propName)) - return e->m_Settings[propName]->FromJSVal(cx, vp, true) ? result.succeed() : result.fail(JSMSG_TYPE_ERR_BAD_ARGS); + return e->m_Settings[propName]->FromJSVal(rq, vp, true) ? result.succeed() : result.fail(JSMSG_TYPE_ERR_BAD_ARGS); LOGERROR("Property '%s' does not exist!", propName.c_str()); return result.fail(JSMSG_UNDEFINED_PROP); @@ -179,17 +180,18 @@ bool JSI_IGUIObject::deleteProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::ObjectOpResult& result) { - IGUIObject* e = ScriptInterface::GetPrivate(cx, obj, &JSI_IGUIObject::JSI_class); + ScriptInterface::Request rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); + + IGUIObject* e = ScriptInterface::GetPrivate(rq, obj, &JSI_IGUIObject::JSI_class); if (!e) return result.fail(JSMSG_NOT_NONNULL_OBJECT); - JSAutoRequest rq(cx); - JS::RootedValue idval(cx); - if (!JS_IdToValue(cx, id, &idval)) + JS::RootedValue idval(rq.cx); + if (!JS_IdToValue(rq.cx, id, &idval)) return result.fail(JSMSG_NOT_NONNULL_OBJECT); std::string propName; - if (!ScriptInterface::FromJSVal(cx, idval, propName)) + if (!ScriptInterface::FromJSVal(rq, idval, propName)) return result.fail(JSMSG_UNDEFINED_PROP); // event handlers @@ -206,21 +208,24 @@ bool JSI_IGUIObject::toString(JSContext* cx, uint argc, JS::Value* vp) { - // No JSAutoRequest needed for these calls + ScriptInterface::Request rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); - IGUIObject* e = ScriptInterface::GetPrivate(cx, args, &JSI_IGUIObject::JSI_class); + IGUIObject* e = ScriptInterface::GetPrivate(rq, args, &JSI_IGUIObject::JSI_class); if (!e) return false; - ScriptInterface::ToJSVal(cx, args.rval(), "[GUIObject: " + e->GetName() + "]"); + + ScriptInterface::ToJSVal(rq, args.rval(), "[GUIObject: " + e->GetName() + "]"); return true; } bool JSI_IGUIObject::focus(JSContext* cx, uint argc, JS::Value* vp) { - // No JSAutoRequest needed for these calls + ScriptInterface::Request rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); - IGUIObject* e = ScriptInterface::GetPrivate(cx, args, &JSI_IGUIObject::JSI_class); + IGUIObject* e = ScriptInterface::GetPrivate(rq, args, &JSI_IGUIObject::JSI_class); if (!e) return false; @@ -231,9 +236,10 @@ bool JSI_IGUIObject::blur(JSContext* cx, uint argc, JS::Value* vp) { - // No JSAutoRequest needed for these calls + ScriptInterface::Request rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); - IGUIObject* e = ScriptInterface::GetPrivate(cx, args, &JSI_IGUIObject::JSI_class); + IGUIObject* e = ScriptInterface::GetPrivate(rq, args, &JSI_IGUIObject::JSI_class); if (!e) return false; @@ -244,15 +250,15 @@ bool JSI_IGUIObject::getComputedSize(JSContext* cx, uint argc, JS::Value* vp) { - JSAutoRequest rq(cx); - JS::CallArgs args = JS::CallArgsFromVp(argc, vp); + ScriptInterface::Request rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); - IGUIObject* e = ScriptInterface::GetPrivate(cx, args, &JSI_IGUIObject::JSI_class); + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); + IGUIObject* e = ScriptInterface::GetPrivate(rq, args, &JSI_IGUIObject::JSI_class); if (!e) return false; e->UpdateCachedSize(); - ScriptInterface::ToJSVal(cx, args.rval(), e->m_CachedActualSize); + ScriptInterface::ToJSVal(rq, args.rval(), e->m_CachedActualSize); return true; } Index: ps/trunk/source/gui/SettingTypes/CGUISize.h =================================================================== --- ps/trunk/source/gui/SettingTypes/CGUISize.h +++ ps/trunk/source/gui/SettingTypes/CGUISize.h @@ -67,8 +67,8 @@ return pixel == other.pixel && percent == other.percent; } - void ToJSVal(JSContext* cx, JS::MutableHandleValue ret) const; - bool FromJSVal(JSContext* cx, JS::HandleValue v); + void ToJSVal(const ScriptInterface::Request& rq, JS::MutableHandleValue ret) const; + bool FromJSVal(const ScriptInterface::Request& rq, JS::HandleValue v); }; #endif // INCLUDED_CGUISIZE Index: ps/trunk/source/gui/SettingTypes/CGUISize.cpp =================================================================== --- ps/trunk/source/gui/SettingTypes/CGUISize.cpp +++ ps/trunk/source/gui/SettingTypes/CGUISize.cpp @@ -140,29 +140,28 @@ return true; } -void CGUISize::ToJSVal(JSContext* cx, JS::MutableHandleValue ret) const +void CGUISize::ToJSVal(const ScriptInterface::Request& rq, JS::MutableHandleValue ret) const { - JSAutoRequest rq(cx); - ScriptInterface* pScriptInterface = ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface; + ScriptInterface* pScriptInterface = ScriptInterface::GetScriptInterfaceAndCBData(rq.cx)->pScriptInterface; ret.setObjectOrNull(pScriptInterface->CreateCustomObject("GUISize")); if (!ret.isObject()) { - JS_ReportError(cx, "CGUISize value is not an Object"); + JS_ReportError(rq.cx, "CGUISize value is not an Object"); return; } - JS::RootedObject obj(cx, &ret.toObject()); - if (!JS_InstanceOf(cx, obj, &JSI_GUISize::JSI_class, nullptr)) + JS::RootedObject obj(rq.cx, &ret.toObject()); + if (!JS_InstanceOf(rq.cx, obj, &JSI_GUISize::JSI_class, nullptr)) { - JS_ReportError(cx, "CGUISize value is not a CGUISize class instance"); + JS_ReportError(rq.cx, "CGUISize value is not a CGUISize class instance"); return; } #define P(x, y, z)\ if (!pScriptInterface->SetProperty(ret, #z, x.y)) \ { \ - JS_ReportError(cx, "Could not SetProperty '%s'", #z); \ + JS_ReportError(rq.cx, "Could not SetProperty '%s'", #z); \ return; \ } P(pixel, left, left); @@ -176,23 +175,22 @@ #undef P } -bool CGUISize::FromJSVal(JSContext* cx, JS::HandleValue v) +bool CGUISize::FromJSVal(const ScriptInterface::Request& rq, JS::HandleValue v) { - JSAutoRequest rq(cx); - ScriptInterface* pScriptInterface = ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface; + ScriptInterface* pScriptInterface = ScriptInterface::GetScriptInterfaceAndCBData(rq.cx)->pScriptInterface; if (v.isString()) { CStrW str; - if (!ScriptInterface::FromJSVal(cx, v, str)) + if (!ScriptInterface::FromJSVal(rq, v, str)) { - JS_ReportError(cx, "CGUISize could not read JS string"); + JS_ReportError(rq.cx, "CGUISize could not read JS string"); return false; } if (!FromString(str.ToUTF8())) { - JS_ReportError(cx, "CGUISize could not parse JS string"); + JS_ReportError(rq.cx, "CGUISize could not parse JS string"); return false; } return true; @@ -200,21 +198,21 @@ if (!v.isObject()) { - JS_ReportError(cx, "CGUISize value is not an String, nor Object"); + JS_ReportError(rq.cx, "CGUISize value is not an String, nor Object"); return false; } - JS::RootedObject obj(cx, &v.toObject()); - if (!JS_InstanceOf(cx, obj, &JSI_GUISize::JSI_class, nullptr)) + JS::RootedObject obj(rq.cx, &v.toObject()); + if (!JS_InstanceOf(rq.cx, obj, &JSI_GUISize::JSI_class, nullptr)) { - JS_ReportError(cx, "CGUISize value is not a CGUISize class instance"); + JS_ReportError(rq.cx, "CGUISize value is not a CGUISize class instance"); return false; } #define P(x, y, z) \ if (!pScriptInterface->GetProperty(v, #z, x.y))\ {\ - JS_ReportError(cx, "CGUISize could not get object property '%s'", #z);\ + JS_ReportError(rq.cx, "CGUISize could not get object property '%s'", #z);\ return false;\ } Index: ps/trunk/source/gui/tests/test_GuiManager.h =================================================================== --- ps/trunk/source/gui/tests/test_GuiManager.h +++ ps/trunk/source/gui/tests/test_GuiManager.h @@ -62,10 +62,9 @@ // Load up a test page. const ScriptInterface& scriptInterface = *(g_GUI->GetScriptInterface()); - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); - JS::RootedValue val(cx); - scriptInterface.CreateObject(cx, &val); + ScriptInterface::Request rq(scriptInterface); + JS::RootedValue val(rq.cx); + scriptInterface.CreateObject(rq, &val); std::shared_ptr data = scriptInterface.WriteStructuredClone(JS::NullHandleValue); g_GUI->PushPage(L"hotkey/page_hotkey.xml", data, JS::UndefinedHandleValue); @@ -84,21 +83,20 @@ in_dispatch_event(&ev); const ScriptInterface& pageScriptInterface = *(g_GUI->GetActiveGUI()->GetScriptInterface()); - JSContext* pcx = pageScriptInterface.GetContext(); - JSAutoRequest pagerq(pcx); - JS::RootedValue global(pcx, pageScriptInterface.GetGlobalObject()); + ScriptInterface::Request prq(pageScriptInterface); + JS::RootedValue global(prq.cx, pageScriptInterface.GetGlobalObject()); // Ensure that our hotkey state was synchronised with the event itself. bool hotkey_pressed_value = false; - JS::RootedValue js_hotkey_pressed_value(pageScriptInterface.GetContext()); + JS::RootedValue js_hotkey_pressed_value(prq.cx); pageScriptInterface.GetProperty(global, "state_before", &js_hotkey_pressed_value); - ScriptInterface::FromJSVal(pcx, js_hotkey_pressed_value, hotkey_pressed_value); + ScriptInterface::FromJSVal(prq, js_hotkey_pressed_value, hotkey_pressed_value); TS_ASSERT_EQUALS(hotkey_pressed_value, true); hotkey_pressed_value = false; pageScriptInterface.GetProperty(global, "state_after", &js_hotkey_pressed_value); - ScriptInterface::FromJSVal(pcx, js_hotkey_pressed_value, hotkey_pressed_value); + ScriptInterface::FromJSVal(prq, js_hotkey_pressed_value, hotkey_pressed_value); TS_ASSERT_EQUALS(hotkey_pressed_value, true); // We are listening to KeyDown events, so repeat shouldn't matter. @@ -109,12 +107,12 @@ hotkey_pressed_value = false; pageScriptInterface.GetProperty(global, "state_before", &js_hotkey_pressed_value); - ScriptInterface::FromJSVal(pcx, js_hotkey_pressed_value, hotkey_pressed_value); + ScriptInterface::FromJSVal(prq, js_hotkey_pressed_value, hotkey_pressed_value); TS_ASSERT_EQUALS(hotkey_pressed_value, true); hotkey_pressed_value = false; pageScriptInterface.GetProperty(global, "state_after", &js_hotkey_pressed_value); - ScriptInterface::FromJSVal(pcx, js_hotkey_pressed_value, hotkey_pressed_value); + ScriptInterface::FromJSVal(prq, js_hotkey_pressed_value, hotkey_pressed_value); TS_ASSERT_EQUALS(hotkey_pressed_value, true); hotkeyNotification.ev.type = SDL_KEYUP; @@ -124,12 +122,12 @@ hotkey_pressed_value = true; pageScriptInterface.GetProperty(global, "state_before", &js_hotkey_pressed_value); - ScriptInterface::FromJSVal(pcx, js_hotkey_pressed_value, hotkey_pressed_value); + ScriptInterface::FromJSVal(prq, js_hotkey_pressed_value, hotkey_pressed_value); TS_ASSERT_EQUALS(hotkey_pressed_value, false); hotkey_pressed_value = true; pageScriptInterface.GetProperty(global, "state_after", &js_hotkey_pressed_value); - ScriptInterface::FromJSVal(pcx, js_hotkey_pressed_value, hotkey_pressed_value); + ScriptInterface::FromJSVal(prq, js_hotkey_pressed_value, hotkey_pressed_value); TS_ASSERT_EQUALS(hotkey_pressed_value, false); } Index: ps/trunk/source/lobby/XmppClient.cpp =================================================================== --- ps/trunk/source/lobby/XmppClient.cpp +++ ps/trunk/source/lobby/XmppClient.cpp @@ -537,18 +537,17 @@ */ void XmppClient::GUIGetPlayerList(const ScriptInterface& scriptInterface, JS::MutableHandleValue ret) { - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(scriptInterface); - ScriptInterface::CreateArray(cx, ret); + ScriptInterface::CreateArray(rq, ret); int j = 0; for (const std::pair& p : m_PlayerMap) { - JS::RootedValue player(cx); + JS::RootedValue player(rq.cx); ScriptInterface::CreateObject( - cx, + rq, &player, "name", p.first, "presence", p.second.m_Presence, @@ -566,10 +565,9 @@ */ void XmppClient::GUIGetGameList(const ScriptInterface& scriptInterface, JS::MutableHandleValue ret) { - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(scriptInterface); - ScriptInterface::CreateArray(cx, ret); + ScriptInterface::CreateArray(rq, ret); int j = 0; const char* stats[] = { "name", "ip", "port", "stunIP", "stunPort", "hostUsername", "state", @@ -578,8 +576,8 @@ for(const glooxwrapper::Tag* const& t : m_GameList) { - JS::RootedValue game(cx); - ScriptInterface::CreateObject(cx, &game); + JS::RootedValue game(rq.cx); + ScriptInterface::CreateObject(rq, &game); for (size_t i = 0; i < ARRAY_SIZE(stats); ++i) scriptInterface.SetProperty(game, stats[i], t->findAttribute(stats[i])); @@ -595,18 +593,17 @@ */ void XmppClient::GUIGetBoardList(const ScriptInterface& scriptInterface, JS::MutableHandleValue ret) { - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(scriptInterface); - ScriptInterface::CreateArray(cx, ret); + ScriptInterface::CreateArray(rq, ret); int j = 0; const char* attributes[] = { "name", "rank", "rating" }; for(const glooxwrapper::Tag* const& t : m_BoardList) { - JS::RootedValue board(cx); - ScriptInterface::CreateObject(cx, &board); + JS::RootedValue board(rq.cx); + ScriptInterface::CreateObject(rq, &board); for (size_t i = 0; i < ARRAY_SIZE(attributes); ++i) scriptInterface.SetProperty(board, attributes[i], t->findAttribute(attributes[i])); @@ -622,18 +619,17 @@ */ void XmppClient::GUIGetProfile(const ScriptInterface& scriptInterface, JS::MutableHandleValue ret) { - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(scriptInterface); - ScriptInterface::CreateArray(cx, ret); + ScriptInterface::CreateArray(rq, ret); int j = 0; const char* stats[] = { "player", "rating", "totalGamesPlayed", "highestRating", "wins", "losses", "rank" }; for (const glooxwrapper::Tag* const& t : m_Profile) { - JS::RootedValue profile(cx); - ScriptInterface::CreateObject(cx, &profile); + JS::RootedValue profile(rq.cx); + ScriptInterface::CreateObject(rq, &profile); for (size_t i = 0; i < ARRAY_SIZE(stats); ++i) scriptInterface.SetProperty(profile, stats[i], t->findAttribute(stats[i])); @@ -646,18 +642,17 @@ * Message interfaces * *****************************************************/ -void SetGUIMessageProperty(JSContext* UNUSED(cx), JS::HandleObject UNUSED(messageObj)) +void SetGUIMessageProperty(const ScriptInterface::Request& UNUSED(rq), JS::HandleObject UNUSED(messageObj)) { } template -void SetGUIMessageProperty(JSContext* cx, JS::HandleObject messageObj, const std::string& propertyName, const T& propertyValue, Args const&... args) +void SetGUIMessageProperty(const ScriptInterface::Request& rq, JS::HandleObject messageObj, const std::string& propertyName, const T& propertyValue, Args const&... args) { - // JSAutoRequest is the responsibility of the caller - JS::RootedValue scriptPropertyValue(cx); - ScriptInterface::AssignOrToJSVal(cx, &scriptPropertyValue, propertyValue); - JS_DefineProperty(cx, messageObj, propertyName.c_str(), scriptPropertyValue, JSPROP_ENUMERATE); - SetGUIMessageProperty(cx, messageObj, args...); + JS::RootedValue scriptPropertyValue(rq.cx); + ScriptInterface::AssignOrToJSVal(rq, &scriptPropertyValue, propertyValue); + JS_DefineProperty(rq.cx, messageObj, propertyName.c_str(), scriptPropertyValue, JSPROP_ENUMERATE); + SetGUIMessageProperty(rq, messageObj, args...); } template @@ -669,19 +664,18 @@ { if (!m_ScriptInterface) return; - JSContext* cx = m_ScriptInterface->GetContext(); - JSAutoRequest rq(cx); - JS::RootedValue message(cx); + ScriptInterface::Request rq(m_ScriptInterface); + JS::RootedValue message(rq.cx); ScriptInterface::CreateObject( - cx, + rq, &message, "type", type, "level", level, "historic", false, "time", static_cast(time)); - JS::RootedObject messageObj(cx, message.toObjectOrNull()); - SetGUIMessageProperty(cx, messageObj, args...); + JS::RootedObject messageObj(rq.cx, message.toObjectOrNull()); + SetGUIMessageProperty(rq, messageObj, args...); m_ScriptInterface->FreezeObject(message, true); m_GuiMessageQueue.push_back(JS::Heap(message)); } @@ -703,13 +697,12 @@ if ((m_isConnected && !m_initialLoadComplete) || m_GuiMessageQueue.empty()) return JS::UndefinedValue(); - JSContext* cx = m_ScriptInterface->GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(m_ScriptInterface); // Optimize for batch message processing that is more // performance demanding than processing a lone message. - JS::RootedValue messages(cx); - ScriptInterface::CreateArray(cx, &messages); + JS::RootedValue messages(rq.cx); + ScriptInterface::CreateArray(rq, &messages); int j = 0; @@ -719,7 +712,7 @@ // Store historic chat messages. // Only store relevant messages to minimize memory footprint. - JS::RootedValue rootedMessage(cx, message); + JS::RootedValue rootedMessage(rq.cx, message); std::string type; m_ScriptInterface->GetProperty(rootedMessage, "type", type); if (type != "chat") @@ -730,8 +723,8 @@ if (level != "room-message" && level != "private-message") continue; - JS::RootedValue historicMessage(cx); - if (JS_StructuredClone(cx, rootedMessage, &historicMessage, nullptr, nullptr)) + JS::RootedValue historicMessage(rq.cx); + if (JS_StructuredClone(rq.cx, rootedMessage, &historicMessage, nullptr, nullptr)) { m_ScriptInterface->SetProperty(historicMessage, "historic", true); m_ScriptInterface->FreezeObject(historicMessage, true); @@ -751,11 +744,10 @@ if (m_HistoricGuiMessages.empty()) return JS::UndefinedValue(); - JSContext* cx = m_ScriptInterface->GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(m_ScriptInterface); - JS::RootedValue messages(cx); - ScriptInterface::CreateArray(cx, &messages); + JS::RootedValue messages(rq.cx); + ScriptInterface::CreateArray(rq, &messages); int j = 0; for (const JS::Heap& message : m_HistoricGuiMessages) Index: ps/trunk/source/lobby/scripting/GlooxScriptConversions.cpp =================================================================== --- ps/trunk/source/lobby/scripting/GlooxScriptConversions.cpp +++ ps/trunk/source/lobby/scripting/GlooxScriptConversions.cpp @@ -23,39 +23,39 @@ #include "lobby/XmppClient.h" #include "scriptinterface/ScriptInterface.h" -template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const glooxwrapper::string& val) +template<> void ScriptInterface::ToJSVal(const Request& rq, JS::MutableHandleValue ret, const glooxwrapper::string& val) { - ToJSVal(cx, ret, wstring_from_utf8(val.to_string())); + ToJSVal(rq, ret, wstring_from_utf8(val.to_string())); } -template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const gloox::Presence::PresenceType& val) +template<> void ScriptInterface::ToJSVal(const Request& rq, JS::MutableHandleValue ret, const gloox::Presence::PresenceType& val) { - ToJSVal(cx, ret, XmppClient::GetPresenceString(val)); + ToJSVal(rq, ret, XmppClient::GetPresenceString(val)); } -template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const gloox::MUCRoomRole& val) +template<> void ScriptInterface::ToJSVal(const Request& rq, JS::MutableHandleValue ret, const gloox::MUCRoomRole& val) { - ToJSVal(cx, ret, XmppClient::GetRoleString(val)); + ToJSVal(rq, ret, XmppClient::GetRoleString(val)); } -template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const gloox::StanzaError& val) +template<> void ScriptInterface::ToJSVal(const Request& rq, JS::MutableHandleValue ret, const gloox::StanzaError& val) { - ToJSVal(cx, ret, wstring_from_utf8(XmppClient::StanzaErrorToString(val))); + ToJSVal(rq, ret, wstring_from_utf8(XmppClient::StanzaErrorToString(val))); } -template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const gloox::ConnectionError& val) +template<> void ScriptInterface::ToJSVal(const Request& rq, JS::MutableHandleValue ret, const gloox::ConnectionError& val) { - ToJSVal(cx, ret, wstring_from_utf8(XmppClient::ConnectionErrorToString(val))); + ToJSVal(rq, ret, wstring_from_utf8(XmppClient::ConnectionErrorToString(val))); } -template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const gloox::RegistrationResult& val) +template<> void ScriptInterface::ToJSVal(const Request& rq, JS::MutableHandleValue ret, const gloox::RegistrationResult& val) { - ToJSVal(cx, ret, wstring_from_utf8(XmppClient::RegistrationResultToString(val))); + ToJSVal(rq, ret, wstring_from_utf8(XmppClient::RegistrationResultToString(val))); } -template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const gloox::CertStatus& val) +template<> void ScriptInterface::ToJSVal(const Request& rq, JS::MutableHandleValue ret, const gloox::CertStatus& val) { - ToJSVal(cx, ret, wstring_from_utf8(XmppClient::CertificateErrorToString(val))); + ToJSVal(rq, ret, wstring_from_utf8(XmppClient::CertificateErrorToString(val))); } #endif // CONFIG2_LOBBY Index: ps/trunk/source/lobby/scripting/JSInterface_Lobby.cpp =================================================================== --- ps/trunk/source/lobby/scripting/JSInterface_Lobby.cpp +++ ps/trunk/source/lobby/scripting/JSInterface_Lobby.cpp @@ -87,9 +87,8 @@ { if (g_XmppClient) { - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); - JS_ReportError(cx, "Cannot call StartXmppClient with an already initialized XmppClient!"); + ScriptInterface::Request rq(pCxPrivate); + JS_ReportError(rq.cx, "Cannot call StartXmppClient with an already initialized XmppClient!"); return; } @@ -109,9 +108,8 @@ { if (g_XmppClient) { - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); - JS_ReportError(cx, "Cannot call StartRegisterXmppClient with an already initialized XmppClient!"); + ScriptInterface::Request rq(pCxPrivate); + JS_ReportError(rq.cx, "Cannot call StartRegisterXmppClient with an already initialized XmppClient!"); return; } @@ -130,9 +128,8 @@ { if (!g_XmppClient) { - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); - JS_ReportError(cx, "Cannot call StopXmppClient without an initialized XmppClient!"); + ScriptInterface::Request rq(pCxPrivate); + JS_ReportError(rq.cx, "Cannot call StopXmppClient without an initialized XmppClient!"); return; } @@ -144,9 +141,8 @@ { if (!g_XmppClient) { - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); - JS_ReportError(cx, "Cannot call ConnectXmppClient without an initialized XmppClient!"); + ScriptInterface::Request rq(pCxPrivate); + JS_ReportError(rq.cx, "Cannot call ConnectXmppClient without an initialized XmppClient!"); return; } @@ -157,9 +153,8 @@ { if (!g_XmppClient) { - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); - JS_ReportError(cx, "Cannot call DisconnectXmppClient without an initialized XmppClient!"); + ScriptInterface::Request rq(pCxPrivate); + JS_ReportError(rq.cx, "Cannot call DisconnectXmppClient without an initialized XmppClient!"); return; } @@ -170,9 +165,8 @@ { if (!g_XmppClient) { - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); - JS_ReportError(cx, "Cannot call IsXmppClientConnected without an initialized XmppClient!"); + ScriptInterface::Request rq(pCxPrivate); + JS_ReportError(rq.cx, "Cannot call IsXmppClientConnected without an initialized XmppClient!"); return false; } @@ -183,9 +177,8 @@ { if (!g_XmppClient) { - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); - JS_ReportError(cx, "Cannot call SendGetBoardList without an initialized XmppClient!"); + ScriptInterface::Request rq(pCxPrivate); + JS_ReportError(rq.cx, "Cannot call SendGetBoardList without an initialized XmppClient!"); return; } @@ -196,9 +189,8 @@ { if (!g_XmppClient) { - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); - JS_ReportError(cx, "Cannot call SendGetProfile without an initialized XmppClient!"); + ScriptInterface::Request rq(pCxPrivate); + JS_ReportError(rq.cx, "Cannot call SendGetProfile without an initialized XmppClient!"); return; } @@ -209,9 +201,8 @@ { if (!g_XmppClient) { - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); - JS_ReportError(cx, "Cannot call SendGameReport without an initialized XmppClient!"); + ScriptInterface::Request rq(pCxPrivate); + JS_ReportError(rq.cx, "Cannot call SendGameReport without an initialized XmppClient!"); return; } @@ -222,9 +213,8 @@ { if (!g_XmppClient) { - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); - JS_ReportError(cx, "Cannot call SendRegisterGame without an initialized XmppClient!"); + ScriptInterface::Request rq(pCxPrivate); + JS_ReportError(rq.cx, "Cannot call SendRegisterGame without an initialized XmppClient!"); return; } @@ -242,9 +232,8 @@ { if (!g_XmppClient) { - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); - JS_ReportError(cx, "Cannot call SendUnregisterGame without an initialized XmppClient!"); + ScriptInterface::Request rq(pCxPrivate); + JS_ReportError(rq.cx, "Cannot call SendUnregisterGame without an initialized XmppClient!"); return; } @@ -255,9 +244,8 @@ { if (!g_XmppClient) { - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); - JS_ReportError(cx, "Cannot call SendChangeStateGame without an initialized XmppClient!"); + ScriptInterface::Request rq(pCxPrivate); + JS_ReportError(rq.cx, "Cannot call SendChangeStateGame without an initialized XmppClient!"); return; } @@ -266,16 +254,15 @@ JS::Value JSI_Lobby::GetPlayerList(ScriptInterface::CxPrivate* pCxPrivate) { - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(pCxPrivate); if (!g_XmppClient) { - JS_ReportError(cx, "Cannot call GetPlayerList without an initialized XmppClient!"); + JS_ReportError(rq.cx, "Cannot call GetPlayerList without an initialized XmppClient!"); return JS::UndefinedValue(); } - JS::RootedValue playerList(cx); + JS::RootedValue playerList(rq.cx); g_XmppClient->GUIGetPlayerList(*(pCxPrivate->pScriptInterface), &playerList); return playerList; @@ -283,16 +270,15 @@ JS::Value JSI_Lobby::GetGameList(ScriptInterface::CxPrivate* pCxPrivate) { - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(pCxPrivate); if (!g_XmppClient) { - JS_ReportError(cx, "Cannot call GetGameList without an initialized XmppClient!"); + JS_ReportError(rq.cx, "Cannot call GetGameList without an initialized XmppClient!"); return JS::UndefinedValue(); } - JS::RootedValue gameList(cx); + JS::RootedValue gameList(rq.cx); g_XmppClient->GUIGetGameList(*(pCxPrivate->pScriptInterface), &gameList); return gameList; @@ -300,16 +286,15 @@ JS::Value JSI_Lobby::GetBoardList(ScriptInterface::CxPrivate* pCxPrivate) { - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(pCxPrivate); if (!g_XmppClient) { - JS_ReportError(cx, "Cannot call GetBoardList without an initialized XmppClient!"); + JS_ReportError(rq.cx, "Cannot call GetBoardList without an initialized XmppClient!"); return JS::UndefinedValue(); } - JS::RootedValue boardList(cx); + JS::RootedValue boardList(rq.cx); g_XmppClient->GUIGetBoardList(*(pCxPrivate->pScriptInterface), &boardList); return boardList; @@ -317,16 +302,15 @@ JS::Value JSI_Lobby::GetProfile(ScriptInterface::CxPrivate* pCxPrivate) { - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(pCxPrivate); if (!g_XmppClient) { - JS_ReportError(cx, "Cannot call GetProfile without an initialized XmppClient!"); + JS_ReportError(rq.cx, "Cannot call GetProfile without an initialized XmppClient!"); return JS::UndefinedValue(); } - JS::RootedValue profileFetch(cx); + JS::RootedValue profileFetch(rq.cx); g_XmppClient->GUIGetProfile(*(pCxPrivate->pScriptInterface), &profileFetch); return profileFetch; @@ -336,9 +320,8 @@ { if (!g_XmppClient) { - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); - JS_ReportError(cx, "Cannot call LobbyGuiPollHasPlayerListUpdate without an initialized XmppClient!"); + ScriptInterface::Request rq(pCxPrivate); + JS_ReportError(rq.cx, "Cannot call LobbyGuiPollHasPlayerListUpdate without an initialized XmppClient!"); return false; } @@ -357,9 +340,8 @@ { if (!g_XmppClient) { - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); - JS_ReportError(cx, "Cannot call LobbyGuiPollHistoricMessages without an initialized XmppClient!"); + ScriptInterface::Request rq(pCxPrivate); + JS_ReportError(rq.cx, "Cannot call LobbyGuiPollHistoricMessages without an initialized XmppClient!"); return JS::UndefinedValue(); } @@ -370,9 +352,8 @@ { if (!g_XmppClient) { - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); - JS_ReportError(cx, "Cannot call LobbySendMessage without an initialized XmppClient!"); + ScriptInterface::Request rq(pCxPrivate); + JS_ReportError(rq.cx, "Cannot call LobbySendMessage without an initialized XmppClient!"); return; } @@ -383,9 +364,8 @@ { if (!g_XmppClient) { - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); - JS_ReportError(cx, "Cannot call LobbySetPlayerPresence without an initialized XmppClient!"); + ScriptInterface::Request rq(pCxPrivate); + JS_ReportError(rq.cx, "Cannot call LobbySetPlayerPresence without an initialized XmppClient!"); return; } @@ -396,9 +376,8 @@ { if (!g_XmppClient) { - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); - JS_ReportError(cx, "Cannot call LobbySetNick without an initialized XmppClient!"); + ScriptInterface::Request rq(pCxPrivate); + JS_ReportError(rq.cx, "Cannot call LobbySetNick without an initialized XmppClient!"); return; } @@ -409,9 +388,8 @@ { if (!g_XmppClient) { - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); - JS_ReportError(cx, "Cannot call LobbyGetNick without an initialized XmppClient!"); + ScriptInterface::Request rq(pCxPrivate); + JS_ReportError(rq.cx, "Cannot call LobbyGetNick without an initialized XmppClient!"); return std::wstring(); } @@ -424,9 +402,8 @@ { if (!g_XmppClient) { - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); - JS_ReportError(cx, "Cannot call LobbyKick without an initialized XmppClient!"); + ScriptInterface::Request rq(pCxPrivate); + JS_ReportError(rq.cx, "Cannot call LobbyKick without an initialized XmppClient!"); return; } @@ -437,9 +414,8 @@ { if (!g_XmppClient) { - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); - JS_ReportError(cx, "Cannot call LobbyBan without an initialized XmppClient!"); + ScriptInterface::Request rq(pCxPrivate); + JS_ReportError(rq.cx, "Cannot call LobbyBan without an initialized XmppClient!"); return; } @@ -450,9 +426,8 @@ { if (!g_XmppClient) { - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); - JS_ReportError(cx, "Cannot call LobbyGetPlayerPresence without an initialized XmppClient!"); + ScriptInterface::Request rq(pCxPrivate); + JS_ReportError(rq.cx, "Cannot call LobbyGetPlayerPresence without an initialized XmppClient!"); return ""; } @@ -463,9 +438,8 @@ { if (!g_XmppClient) { - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); - JS_ReportError(cx, "Cannot call LobbyGetPlayerRole without an initialized XmppClient!"); + ScriptInterface::Request rq(pCxPrivate); + JS_ReportError(rq.cx, "Cannot call LobbyGetPlayerRole without an initialized XmppClient!"); return ""; } @@ -476,9 +450,8 @@ { if (!g_XmppClient) { - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); - JS_ReportError(cx, "Cannot call LobbyGetPlayerRating without an initialized XmppClient!"); + ScriptInterface::Request rq(pCxPrivate); + JS_ReportError(rq.cx, "Cannot call LobbyGetPlayerRating without an initialized XmppClient!"); return std::wstring(); } @@ -536,9 +509,8 @@ { if (!g_XmppClient) { - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); - JS_ReportError(cx, "Cannot call LobbyGetRoomSubject without an initialized XmppClient!"); + ScriptInterface::Request rq(pCxPrivate); + JS_ReportError(rq.cx, "Cannot call LobbyGetRoomSubject without an initialized XmppClient!"); return std::wstring(); } Index: ps/trunk/source/main.cpp =================================================================== --- ps/trunk/source/main.cpp +++ ps/trunk/source/main.cpp @@ -213,8 +213,7 @@ // dispatch all pending events to the various receivers. static void PumpEvents() { - JSContext* cx = g_GUI->GetScriptInterface()->GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(g_GUI->GetScriptInterface()); PROFILE3("dispatch events"); @@ -224,8 +223,8 @@ PROFILE2("event"); if (g_GUI) { - JS::RootedValue tmpVal(cx); - ScriptInterface::ToJSVal(cx, &tmpVal, ev); + JS::RootedValue tmpVal(rq.cx); + ScriptInterface::ToJSVal(rq, &tmpVal, ev); std::string data = g_GUI->GetScriptInterface()->StringifyJSON(&tmpVal); PROFILE2_ATTR("%s", data.c_str()); } Index: ps/trunk/source/network/NetClient.h =================================================================== --- ps/trunk/source/network/NetClient.h +++ ps/trunk/source/network/NetClient.h @@ -156,11 +156,10 @@ template void PushGuiMessage(Args const&... args) { - JSContext* cx = GetScriptInterface().GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(GetScriptInterface()); - JS::RootedValue message(cx); - ScriptInterface::CreateObject(cx, &message, args...); + JS::RootedValue message(rq.cx); + ScriptInterface::CreateObject(rq, &message, args...); m_GuiMessageQueue.push_back(JS::Heap(message)); } Index: ps/trunk/source/network/NetClient.cpp =================================================================== --- ps/trunk/source/network/NetClient.cpp +++ ps/trunk/source/network/NetClient.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -71,7 +71,7 @@ m_Session(NULL), m_UserName(L"anonymous"), m_HostID((u32)-1), m_ClientTurnManager(NULL), m_Game(game), - m_GameAttributes(game->GetSimulation2()->GetScriptInterface().GetContext()), + m_GameAttributes(game->GetSimulation2()->GetScriptInterface().GetJSRuntime()), m_IsLocalClient(isLocalClient), m_LastConnectionCheck(0), m_Rejoin(false) @@ -251,11 +251,10 @@ std::string CNetClient::TestReadGuiMessages() { - JSContext* cx = GetScriptInterface().GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(GetScriptInterface()); std::string r; - JS::RootedValue msg(cx); + JS::RootedValue msg(rq.cx); while (true) { GuiPoll(&msg); @@ -273,18 +272,17 @@ void CNetClient::PostPlayerAssignmentsToScript() { - JSContext* cx = GetScriptInterface().GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(GetScriptInterface()); - JS::RootedValue newAssignments(cx); - ScriptInterface::CreateObject(cx, &newAssignments); + JS::RootedValue newAssignments(rq.cx); + ScriptInterface::CreateObject(rq, &newAssignments); for (const std::pair& p : m_PlayerAssignments) { - JS::RootedValue assignment(cx); + JS::RootedValue assignment(rq.cx); ScriptInterface::CreateObject( - cx, + rq, &assignment, "name", p.second.m_Name, "player", p.second.m_PlayerID, Index: ps/trunk/source/network/NetServer.cpp =================================================================== --- ps/trunk/source/network/NetServer.cpp +++ ps/trunk/source/network/NetServer.cpp @@ -419,8 +419,7 @@ m_ScriptInterface->GetRuntime()->MaybeIncrementalGC(0.5f); - JSContext* cx = m_ScriptInterface->GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(m_ScriptInterface); std::vector newStartGame; std::vector newGameAttributes; @@ -441,7 +440,7 @@ if (!newGameAttributes.empty()) { - JS::RootedValue gameAttributesVal(cx); + JS::RootedValue gameAttributesVal(rq.cx); GetScriptInterface().ParseJSON(newGameAttributes.back(), &gameAttributesVal); UpdateGameAttributes(&gameAttributesVal); } @@ -1138,9 +1137,8 @@ // unless cheating is enabled bool cheatsEnabled = false; const ScriptInterface& scriptInterface = server.GetScriptInterface(); - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); - JS::RootedValue settings(cx); + ScriptInterface::Request rq(scriptInterface); + JS::RootedValue settings(rq.cx); scriptInterface.GetProperty(server.m_GameAttributes, "settings", &settings); if (scriptInterface.HasProperty(settings, "CheatsEnabled")) scriptInterface.GetProperty(settings, "CheatsEnabled", cheatsEnabled); Index: ps/trunk/source/network/StunClient.cpp =================================================================== --- ps/trunk/source/network/StunClient.cpp +++ ps/trunk/source/network/StunClient.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * Copyright (C) 2013-2016 SuperTuxKart-Team. * This file is part of 0 A.D. * @@ -390,11 +390,10 @@ addr.host = ntohl(m_IP); enet_address_get_host_ip(&addr, ipStr, ARRAY_SIZE(ipStr)); - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(scriptInterface); - JS::RootedValue stunEndpoint(cx); - ScriptInterface::CreateObject(cx, &stunEndpoint, "ip", ipStr, "port", m_Port); + JS::RootedValue stunEndpoint(rq.cx); + ScriptInterface::CreateObject(rq, &stunEndpoint, "ip", ipStr, "port", m_Port); return stunEndpoint; } Index: ps/trunk/source/network/scripting/JSInterface_Network.cpp =================================================================== --- ps/trunk/source/network/scripting/JSInterface_Network.cpp +++ ps/trunk/source/network/scripting/JSInterface_Network.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -155,9 +155,8 @@ return JS::UndefinedValue(); // Convert from net client context to GUI script context - JSContext* cxNet = g_NetClient->GetScriptInterface().GetContext(); - JSAutoRequest rqNet(cxNet); - JS::RootedValue pollNet(cxNet); + ScriptInterface::Request rqNet(g_NetClient->GetScriptInterface()); + JS::RootedValue pollNet(rqNet.cx); g_NetClient->GuiPoll(&pollNet); return pCxPrivate->pScriptInterface->CloneValueFromOtherContext(g_NetClient->GetScriptInterface(), pollNet); } @@ -167,9 +166,8 @@ ENSURE(g_NetClient); // TODO: This is a workaround because we need to pass a MutableHandle to a JSAPI functions somewhere (with no obvious reason). - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); - JS::RootedValue attribs(cx, attribs1); + ScriptInterface::Request rq(pCxPrivate); + JS::RootedValue attribs(rq.cx, attribs1); g_NetClient->SendGameSetupMessage(&attribs, *(pCxPrivate->pScriptInterface)); } Index: ps/trunk/source/network/tests/test_Net.h =================================================================== --- ps/trunk/source/network/tests/test_Net.h +++ ps/trunk/source/network/tests/test_Net.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -137,8 +137,7 @@ // and prints a load of debug output so you can see if anything funny's going on ScriptInterface scriptInterface("Engine", "Test", g_ScriptRuntime); - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(scriptInterface); TestStdoutLogger logger; @@ -150,9 +149,9 @@ CNetServer server; - JS::RootedValue attrs(cx); + JS::RootedValue attrs(rq.cx); ScriptInterface::CreateObject( - cx, + rq, &attrs, "mapType", "scenario", "map", "maps/scenarios/Saharan Oases", @@ -184,9 +183,9 @@ wait(clients, 100); { - JS::RootedValue cmd(cx); + JS::RootedValue cmd(rq.cx); ScriptInterface::CreateObject( - cx, + rq, &cmd, "type", "debug-print", "message", "[>>> client1 test sim command]\\n"); @@ -194,9 +193,9 @@ } { - JS::RootedValue cmd(cx); + JS::RootedValue cmd(rq.cx); ScriptInterface::CreateObject( - cx, + rq, &cmd, "type", "debug-print", "message", "[>>> client2 test sim command]\\n"); @@ -217,8 +216,7 @@ void test_rejoin_DISABLED() { ScriptInterface scriptInterface("Engine", "Test", g_ScriptRuntime); - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(scriptInterface); TestStdoutLogger logger; @@ -230,9 +228,9 @@ CNetServer server; - JS::RootedValue attrs(cx); + JS::RootedValue attrs(rq.cx); ScriptInterface::CreateObject( - cx, + rq, &attrs, "mapType", "scenario", "map", "maps/scenarios/Saharan Oases", @@ -268,9 +266,9 @@ wait(clients, 100); { - JS::RootedValue cmd(cx); + JS::RootedValue cmd(rq.cx); ScriptInterface::CreateObject( - cx, + rq, &cmd, "type", "debug-print", "message", "[>>> client1 test sim command 1]\\n"); @@ -285,9 +283,9 @@ wait(clients, 100); { - JS::RootedValue cmd(cx); + JS::RootedValue cmd(rq.cx); ScriptInterface::CreateObject( - cx, + rq, &cmd, "type", "debug-print", "message", "[>>> client1 test sim command 2]\\n"); @@ -344,9 +342,9 @@ // CTurnManager::TurnNeedsFullHash to always return true) { - JS::RootedValue cmd(cx); + JS::RootedValue cmd(rq.cx); ScriptInterface::CreateObject( - cx, + rq, &cmd, "type", "debug-print", "message", "[>>> client1 test sim command 3]\\n"); @@ -361,9 +359,9 @@ wait(clients, 100); { - JS::RootedValue cmd(cx); + JS::RootedValue cmd(rq.cx); ScriptInterface::CreateObject( - cx, + rq, &cmd, "type", "debug-print", "message", "[>>> client1 test sim command 4]\\n"); Index: ps/trunk/source/network/tests/test_NetMessage.h =================================================================== --- ps/trunk/source/network/tests/test_NetMessage.h +++ ps/trunk/source/network/tests/test_NetMessage.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -27,11 +27,10 @@ void test_sim() { ScriptInterface script("Test", "Test", g_ScriptRuntime); - JSContext* cx = script.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(script); - JS::RootedValue val(cx); - ScriptInterface::CreateArray(cx, &val); + JS::RootedValue val(rq.cx); + ScriptInterface::CreateArray(rq, &val); script.SetPropertyInt(val, 0, 4); CSimulationMessage msg(script, 1, 2, 3, val); Index: ps/trunk/source/ps/CConsole.cpp =================================================================== --- ps/trunk/source/ps/CConsole.cpp +++ ps/trunk/source/ps/CConsole.cpp @@ -544,12 +544,8 @@ void CConsole::ProcessBuffer(const wchar_t* szLine) { - shared_ptr pScriptInterface = g_GUI->GetActiveGUI()->GetScriptInterface(); - JSContext* cx = pScriptInterface->GetContext(); - JSAutoRequest rq(cx); - - if (szLine == NULL) return; - if (wcslen(szLine) <= 0) return; + if (!szLine || wcslen(szLine) <= 0) + return; ENSURE(wcslen(szLine) < CONSOLE_BUFFER_SIZE); @@ -558,8 +554,10 @@ // a crash it's a useful record. // Process it as JavaScript + shared_ptr pScriptInterface = g_GUI->GetActiveGUI()->GetScriptInterface(); + ScriptInterface::Request rq(*pScriptInterface); - JS::RootedValue rval(cx); + JS::RootedValue rval(rq.cx); pScriptInterface->Eval(szLine, &rval); if (!rval.isUndefined()) InsertMessage(pScriptInterface->ToString(&rval)); Index: ps/trunk/source/ps/Game.cpp =================================================================== --- ps/trunk/source/ps/Game.cpp +++ ps/trunk/source/ps/Game.cpp @@ -191,10 +191,9 @@ std::getline(*m_ReplayStream, line); const ScriptInterface& scriptInterface = m_Simulation2->GetScriptInterface(); - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(scriptInterface); - JS::RootedValue attribs(cx); + JS::RootedValue attribs(rq.cx); scriptInterface.ParseJSON(line, &attribs); StartGame(&attribs, ""); @@ -209,8 +208,7 @@ void CGame::RegisterInit(const JS::HandleValue attribs, const std::string& savedState) { const ScriptInterface& scriptInterface = m_Simulation2->GetScriptInterface(); - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(scriptInterface); m_InitialSavedState = savedState; m_IsSavedGame = !savedState.empty(); @@ -240,7 +238,7 @@ { // Load random map attributes std::wstring scriptFile; - JS::RootedValue settings(cx); + JS::RootedValue settings(rq.cx); scriptInterface.GetProperty(attribs, "script", scriptFile); scriptInterface.GetProperty(attribs, "settings", &settings); @@ -250,7 +248,7 @@ else { std::wstring mapFile; - JS::RootedValue settings(cx); + JS::RootedValue settings(rq.cx); scriptInterface.GetProperty(attribs, "map", mapFile); scriptInterface.GetProperty(attribs, "settings", &settings); @@ -325,9 +323,9 @@ if (g_GUI && g_GUI->GetPageCount()) { shared_ptr scriptInterface = g_GUI->GetActiveGUI()->GetScriptInterface(); - JSContext* cx = scriptInterface->GetContext(); - JSAutoRequest rq(cx); - JS::RootedValue global(cx, scriptInterface->GetGlobalObject()); + ScriptInterface::Request rq(scriptInterface); + + JS::RootedValue global(rq.cx, scriptInterface->GetGlobalObject()); if (scriptInterface->HasProperty(global, "reallyStartGame")) scriptInterface->CallFunctionVoid(global, "reallyStartGame"); } Index: ps/trunk/source/ps/GameSetup/GameSetup.cpp =================================================================== --- ps/trunk/source/ps/GameSetup/GameSetup.cpp +++ ps/trunk/source/ps/GameSetup/GameSetup.cpp @@ -184,15 +184,14 @@ void GUI_DisplayLoadProgress(int percent, const wchar_t* pending_task) { const ScriptInterface& scriptInterface = *(g_GUI->GetActiveGUI()->GetScriptInterface()); - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(scriptInterface); - JS::AutoValueVector paramData(cx); + JS::AutoValueVector paramData(rq.cx); paramData.append(JS::NumberValue(percent)); - JS::RootedValue valPendingTask(cx); - scriptInterface.ToJSVal(cx, &valPendingTask, pending_task); + JS::RootedValue valPendingTask(rq.cx); + scriptInterface.ToJSVal(rq, &valPendingTask, pending_task); paramData.append(valPendingTask); g_GUI->SendEventToAll(g_EventNameGameLoadProgress, paramData); @@ -517,23 +516,22 @@ { // The GUI has not been initialized yet, so use the simulation scriptinterface for this variable ScriptInterface& scriptInterface = g_Game->GetSimulation2()->GetScriptInterface(); - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(scriptInterface); - JS::RootedValue playerAssignments(cx); - ScriptInterface::CreateObject(cx, &playerAssignments); + JS::RootedValue playerAssignments(rq.cx); + ScriptInterface::CreateObject(rq, &playerAssignments); if (!networked) { - JS::RootedValue localPlayer(cx); - ScriptInterface::CreateObject(cx, &localPlayer, "player", g_Game->GetPlayerID()); + JS::RootedValue localPlayer(rq.cx); + ScriptInterface::CreateObject(rq, &localPlayer, "player", g_Game->GetPlayerID()); scriptInterface.SetProperty(playerAssignments, "local", localPlayer); } - JS::RootedValue sessionInitData(cx); + JS::RootedValue sessionInitData(rq.cx); ScriptInterface::CreateObject( - cx, + rq, &sessionInitData, "attribs", attrs, "playerAssignments", playerAssignments); @@ -1061,12 +1059,11 @@ const bool setup_gui = ((flags & INIT_NO_GUI) == 0); // We only want to display the splash screen at startup shared_ptr scriptInterface = g_GUI->GetScriptInterface(); - JSContext* cx = scriptInterface->GetContext(); - JSAutoRequest rq(cx); - JS::RootedValue data(cx); + ScriptInterface::Request rq(scriptInterface); + JS::RootedValue data(rq.cx); if (g_GUI) { - ScriptInterface::CreateObject(cx, &data, "isStartup", true); + ScriptInterface::CreateObject(rq, &data, "isStartup", true); if (!installedMods.empty()) scriptInterface->SetProperty(data, "installedMods", installedMods); } @@ -1221,16 +1218,15 @@ g_Game = new CGame(!args.Has("autostart-disable-replay")); ScriptInterface& scriptInterface = g_Game->GetSimulation2()->GetScriptInterface(); - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(scriptInterface); - JS::RootedValue attrs(cx); - JS::RootedValue settings(cx); - JS::RootedValue playerData(cx); - - ScriptInterface::CreateObject(cx, &attrs); - ScriptInterface::CreateObject(cx, &settings); - ScriptInterface::CreateArray(cx, &playerData); + JS::RootedValue attrs(rq.cx); + JS::RootedValue settings(rq.cx); + JS::RootedValue playerData(rq.cx); + + ScriptInterface::CreateObject(rq, &attrs); + ScriptInterface::CreateObject(rq, &settings); + ScriptInterface::CreateArray(rq, &playerData); // The directory in front of the actual map name indicates which type // of map is being loaded. Drawback of this approach is the association @@ -1245,7 +1241,7 @@ { // Random map definition will be loaded from JSON file, so we need to parse it std::wstring scriptPath = L"maps/" + autoStartName.FromUTF8() + L".json"; - JS::RootedValue scriptData(cx); + JS::RootedValue scriptData(rq.cx); scriptInterface.ReadJSONFile(scriptPath, &scriptData); if (!scriptData.isUndefined() && scriptInterface.GetProperty(scriptData, "settings", &settings)) { @@ -1282,11 +1278,11 @@ // Set up player data for (size_t i = 0; i < numPlayers; ++i) { - JS::RootedValue player(cx); + JS::RootedValue player(rq.cx); // We could load player_defaults.json here, but that would complicate the logic // even more and autostart is only intended for developers anyway - ScriptInterface::CreateObject(cx, &player, "Civ", "athen"); + ScriptInterface::CreateObject(rq, &player, "Civ", "athen"); scriptInterface.SetPropertyInt(playerData, i, player); } @@ -1348,7 +1344,7 @@ // attrs.settings = { PlayerData: [ { AI: ... }, ... ] } // or = { PlayerData: [ null, { AI: ... }, ... ] } when gaia set int offset = 1; - JS::RootedValue player(cx); + JS::RootedValue player(rq.cx); if (scriptInterface.GetPropertyInt(playerData, 0, &player) && player.isNull()) offset = 0; @@ -1361,7 +1357,7 @@ int playerID = civArgs[i].BeforeFirst(":").ToInt(); // Instead of overwriting existing player data, modify the array - JS::RootedValue player(cx); + JS::RootedValue player(rq.cx); if (!scriptInterface.GetPropertyInt(playerData, playerID-offset, &player) || player.isUndefined()) { if (mapDirectory == L"skirmishes") @@ -1370,7 +1366,7 @@ LOGWARNING("Autostart: Invalid player %d in autostart-team option", playerID); continue; } - ScriptInterface::CreateObject(cx, &player); + ScriptInterface::CreateObject(rq, &player); } int teamID = civArgs[i].AfterFirst(":").ToInt() - 1; @@ -1392,7 +1388,7 @@ int playerID = aiArgs[i].BeforeFirst(":").ToInt(); // Instead of overwriting existing player data, modify the array - JS::RootedValue player(cx); + JS::RootedValue player(rq.cx); if (!scriptInterface.GetPropertyInt(playerData, playerID-offset, &player) || player.isUndefined()) { if (mapDirectory == L"scenarios" || mapDirectory == L"skirmishes") @@ -1401,7 +1397,7 @@ LOGWARNING("Autostart: Invalid player %d in autostart-ai option", playerID); continue; } - ScriptInterface::CreateObject(cx, &player); + ScriptInterface::CreateObject(rq, &player); } scriptInterface.SetProperty(player, "AI", aiArgs[i].AfterFirst(":")); @@ -1419,7 +1415,7 @@ int playerID = civArgs[i].BeforeFirst(":").ToInt(); // Instead of overwriting existing player data, modify the array - JS::RootedValue player(cx); + JS::RootedValue player(rq.cx); if (!scriptInterface.GetPropertyInt(playerData, playerID-offset, &player) || player.isUndefined()) { if (mapDirectory == L"scenarios" || mapDirectory == L"skirmishes") @@ -1428,7 +1424,7 @@ LOGWARNING("Autostart: Invalid player %d in autostart-aidiff option", playerID); continue; } - ScriptInterface::CreateObject(cx, &player); + ScriptInterface::CreateObject(rq, &player); } scriptInterface.SetProperty(player, "AIDiff", civArgs[i].AfterFirst(":").ToInt()); @@ -1446,7 +1442,7 @@ int playerID = civArgs[i].BeforeFirst(":").ToInt(); // Instead of overwriting existing player data, modify the array - JS::RootedValue player(cx); + JS::RootedValue player(rq.cx); if (!scriptInterface.GetPropertyInt(playerData, playerID-offset, &player) || player.isUndefined()) { if (mapDirectory == L"skirmishes") @@ -1455,7 +1451,7 @@ LOGWARNING("Autostart: Invalid player %d in autostart-civ option", playerID); continue; } - ScriptInterface::CreateObject(cx, &player); + ScriptInterface::CreateObject(rq, &player); } scriptInterface.SetProperty(player, "Civ", civArgs[i].AfterFirst(":")); @@ -1479,12 +1475,12 @@ // Add additional scripts to the TriggerScripts property std::vector triggerScriptsVector; - JS::RootedValue triggerScripts(cx); + JS::RootedValue triggerScripts(rq.cx); if (scriptInterface.HasProperty(settings, "TriggerScripts")) { scriptInterface.GetProperty(settings, "TriggerScripts", &triggerScripts); - FromJSVal_vector(cx, triggerScripts, triggerScriptsVector); + FromJSVal_vector(rq, triggerScripts, triggerScriptsVector); } if (!CRenderer::IsInitialised()) @@ -1504,9 +1500,9 @@ for (const CStr& victory : victoryConditions) { - JS::RootedValue scriptData(cx); - JS::RootedValue data(cx); - JS::RootedValue victoryScripts(cx); + JS::RootedValue scriptData(rq.cx); + JS::RootedValue data(rq.cx); + JS::RootedValue victoryScripts(rq.cx); CStrW scriptPath = L"simulation/data/settings/victory_conditions/" + victory.FromUTF8() + L".json"; scriptInterface.ReadJSONFile(scriptPath, &scriptData); @@ -1514,7 +1510,7 @@ && scriptInterface.GetProperty(data, "Scripts", &victoryScripts) && !victoryScripts.isUndefined()) { std::vector victoryScriptsVector; - FromJSVal_vector(cx, victoryScripts, victoryScriptsVector); + FromJSVal_vector(rq, victoryScripts, victoryScriptsVector); triggerScriptsVector.insert(triggerScriptsVector.end(), victoryScriptsVector.begin(), victoryScriptsVector.end()); } else @@ -1524,7 +1520,7 @@ } } - ToJSVal_vector(cx, &triggerScripts, triggerScriptsVector); + ToJSVal_vector(rq, &triggerScripts, triggerScriptsVector); scriptInterface.SetProperty(settings, "TriggerScripts", triggerScripts); int wonderDuration = 10; @@ -1606,9 +1602,8 @@ g_Game->StartVisualReplay(replayFile); ScriptInterface& scriptInterface = g_Game->GetSimulation2()->GetScriptInterface(); - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); - JS::RootedValue attrs(cx, g_Game->GetSimulation2()->GetInitAttributes()); + ScriptInterface::Request rq(scriptInterface); + JS::RootedValue attrs(rq.cx, g_Game->GetSimulation2()->GetInitAttributes()); InitPsAutostart(false, attrs); @@ -1618,10 +1613,9 @@ void CancelLoad(const CStrW& message) { shared_ptr pScriptInterface = g_GUI->GetActiveGUI()->GetScriptInterface(); - JSContext* cx = pScriptInterface->GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(pScriptInterface); - JS::RootedValue global(cx, pScriptInterface->GetGlobalObject()); + JS::RootedValue global(rq.cx, pScriptInterface->GetGlobalObject()); LDR_Cancel(); Index: ps/trunk/source/ps/GameSetup/HWDetect.cpp =================================================================== --- ps/trunk/source/ps/GameSetup/HWDetect.cpp +++ ps/trunk/source/ps/GameSetup/HWDetect.cpp @@ -78,10 +78,9 @@ #if ARCH_X86_X64 void ConvertCaches(const ScriptInterface& scriptInterface, x86_x64::IdxCache idxCache, JS::MutableHandleValue ret) { - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(scriptInterface); - ScriptInterface::CreateArray(cx, ret); + ScriptInterface::CreateArray(rq, ret); for (size_t idxLevel = 0; idxLevel < x86_x64::Cache::maxLevels; ++idxLevel) { @@ -89,10 +88,10 @@ if (pcache->m_Type == x86_x64::Cache::kNull || pcache->m_NumEntries == 0) continue; - JS::RootedValue cache(cx); + JS::RootedValue cache(rq.cx); ScriptInterface::CreateObject( - cx, + rq, &cache, "type", static_cast(pcache->m_Type), "level", static_cast(pcache->m_Level), @@ -107,10 +106,9 @@ void ConvertTLBs(const ScriptInterface& scriptInterface, JS::MutableHandleValue ret) { - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(scriptInterface); - ScriptInterface::CreateArray(cx, ret); + ScriptInterface::CreateArray(rq, ret); for(size_t i = 0; ; i++) { @@ -118,10 +116,10 @@ if (!ptlb) break; - JS::RootedValue tlb(cx); + JS::RootedValue tlb(rq.cx); ScriptInterface::CreateObject( - cx, + rq, &tlb, "type", static_cast(ptlb->m_Type), "level", static_cast(ptlb->m_Level), @@ -144,8 +142,8 @@ TIMER(L"RunHardwareDetection"); ScriptInterface scriptInterface("Engine", "HWDetect", g_ScriptRuntime); - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); + + ScriptInterface::Request rq(scriptInterface); JSI_Debug::RegisterScriptFunctions(scriptInterface); // Engine.DisplayErrorDialog JSI_ConfigDB::RegisterScriptFunctions(scriptInterface); @@ -169,8 +167,8 @@ // (We'll use this same data for the opt-in online reporting system, so it // includes some fields that aren't directly useful for the hwdetect script) - JS::RootedValue settings(cx); - ScriptInterface::CreateObject(cx, &settings); + JS::RootedValue settings(rq.cx); + ScriptInterface::CreateObject(rq, &settings); scriptInterface.SetProperty(settings, "os_unix", OS_UNIX); scriptInterface.SetProperty(settings, "os_bsd", OS_BSD); @@ -263,7 +261,7 @@ scriptInterface.SetProperty(settings, "x86_caps[2]", caps2); scriptInterface.SetProperty(settings, "x86_caps[3]", caps3); - JS::RootedValue tmpVal(cx); + JS::RootedValue tmpVal(rq.cx); ConvertCaches(scriptInterface, x86_x64::L1I, &tmpVal); scriptInterface.SetProperty(settings, "x86_icaches", tmpVal); ConvertCaches(scriptInterface, x86_x64::L1D, &tmpVal); @@ -285,7 +283,7 @@ scriptInterface.StringifyJSON(&settings, true)); // Run the detection script: - JS::RootedValue global(cx, scriptInterface.GetGlobalObject()); + JS::RootedValue global(rq.cx, scriptInterface.GetGlobalObject()); scriptInterface.CallFunctionVoid(global, "RunHardwareDetection", settings); } Index: ps/trunk/source/ps/Mod.cpp =================================================================== --- ps/trunk/source/ps/Mod.cpp +++ ps/trunk/source/ps/Mod.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -39,9 +39,8 @@ JS::Value Mod::GetAvailableMods(const ScriptInterface& scriptInterface) { - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); - JS::RootedObject obj(cx, JS_NewPlainObject(cx)); + ScriptInterface::Request rq(scriptInterface); + JS::RootedObject obj(rq.cx, JS_NewPlainObject(rq.cx)); const Paths paths(g_args); @@ -68,12 +67,12 @@ if (modinfo.Load(vfs, L"mod.json", false) != PSRETURN_OK) continue; - JS::RootedValue json(cx); + JS::RootedValue json(rq.cx); if (!scriptInterface.ParseJSON(modinfo.GetAsString(), &json)) continue; // Valid mod, add it to our structure - JS_SetProperty(cx, obj, utf8_from_wstring(iter->string()).c_str(), json); + JS_SetProperty(rq.cx, obj, utf8_from_wstring(iter->string()).c_str(), json); } GetDirectoryEntries(modUserPath, NULL, &modDirsUser); @@ -94,12 +93,12 @@ if (modinfo.Load(vfs, L"mod.json", false) != PSRETURN_OK) continue; - JS::RootedValue json(cx); + JS::RootedValue json(rq.cx); if (!scriptInterface.ParseJSON(modinfo.GetAsString(), &json)) continue; // Valid mod, add it to our structure - JS_SetProperty(cx, obj, utf8_from_wstring(iter->string()).c_str(), json); + JS_SetProperty(rq.cx, obj, utf8_from_wstring(iter->string()).c_str(), json); } return JS::ObjectValue(*obj); @@ -108,10 +107,9 @@ void Mod::CacheEnabledModVersions(const shared_ptr& scriptRuntime) { ScriptInterface scriptInterface("Engine", "CacheEnabledModVersions", scriptRuntime); - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(scriptInterface); - JS::RootedValue availableMods(cx, GetAvailableMods(scriptInterface)); + JS::RootedValue availableMods(rq.cx, GetAvailableMods(scriptInterface)); g_LoadedModVersions.clear(); @@ -122,7 +120,7 @@ continue; CStr version; - JS::RootedValue modData(cx); + JS::RootedValue modData(rq.cx); if (scriptInterface.GetProperty(availableMods, mod.c_str(), &modData)) scriptInterface.GetProperty(modData, "version", version); @@ -132,23 +130,21 @@ JS::Value Mod::GetLoadedModsWithVersions(const ScriptInterface& scriptInterface) { - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); - JS::RootedValue returnValue(cx); - scriptInterface.ToJSVal(cx, &returnValue, g_LoadedModVersions); + ScriptInterface::Request rq(scriptInterface); + JS::RootedValue returnValue(rq.cx); + scriptInterface.ToJSVal(rq, &returnValue, g_LoadedModVersions); return returnValue; } JS::Value Mod::GetEngineInfo(const ScriptInterface& scriptInterface) { - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(scriptInterface); - JS::RootedValue mods(cx, Mod::GetLoadedModsWithVersions(scriptInterface)); - JS::RootedValue metainfo(cx); + JS::RootedValue mods(rq.cx, Mod::GetLoadedModsWithVersions(scriptInterface)); + JS::RootedValue metainfo(rq.cx); ScriptInterface::CreateObject( - cx, + rq, &metainfo, "engine_version", engine_version, "mods", mods); Index: ps/trunk/source/ps/ModInstaller.cpp =================================================================== --- ps/trunk/source/ps/ModInstaller.cpp +++ ps/trunk/source/ps/ModInstaller.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2018 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -61,19 +61,22 @@ return FAIL_ON_MOD_LOAD; // Extract the name of the mod - ScriptInterface scriptInterface("Engine", "ModInstaller", scriptRuntime); - JSContext* cx = scriptInterface.GetContext(); - JS::RootedValue json_val(cx); - if (!scriptInterface.ParseJSON(modinfo.GetAsString(), &json_val)) - return FAIL_ON_PARSE_JSON; - JS::RootedObject json_obj(cx, json_val.toObjectOrNull()); - JS::RootedValue name_val(cx); - if (!JS_GetProperty(cx, json_obj, "name", &name_val)) - return FAIL_ON_EXTRACT_NAME; CStr modName; - ScriptInterface::FromJSVal(cx, name_val, modName); - if (modName.empty()) - return FAIL_ON_EXTRACT_NAME; + { + ScriptInterface scriptInterface("Engine", "ModInstaller", scriptRuntime); + ScriptInterface::Request rq(scriptInterface); + + JS::RootedValue json_val(rq.cx); + if (!scriptInterface.ParseJSON(modinfo.GetAsString(), &json_val)) + return FAIL_ON_PARSE_JSON; + JS::RootedObject json_obj(rq.cx, json_val.toObjectOrNull()); + JS::RootedValue name_val(rq.cx); + if (!JS_GetProperty(rq.cx, json_obj, "name", &name_val)) + return FAIL_ON_EXTRACT_NAME; + ScriptInterface::FromJSVal(rq, name_val, modName); + if (modName.empty()) + return FAIL_ON_EXTRACT_NAME; + } const OsPath modDir = m_ModsDir / modName; const OsPath modPath = modDir / (modName + ".zip"); Index: ps/trunk/source/ps/ModIo.cpp =================================================================== --- ps/trunk/source/ps/ModIo.cpp +++ ps/trunk/source/ps/ModIo.cpp @@ -586,10 +586,9 @@ bool ModIo::ParseGameIdResponse(const ScriptInterface& scriptInterface, const std::string& responseData, int& id, std::string& err) { #define CLEANUP() id = -1; - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(scriptInterface); - JS::RootedValue gameResponse(cx); + JS::RootedValue gameResponse(rq.cx); if (!scriptInterface.ParseJSON(responseData, &gameResponse)) FAIL("Failed to parse response as JSON."); @@ -597,35 +596,35 @@ if (!gameResponse.isObject()) FAIL("response not an object."); - JS::RootedObject gameResponseObj(cx, gameResponse.toObjectOrNull()); - JS::RootedValue dataVal(cx); - if (!JS_GetProperty(cx, gameResponseObj, "data", &dataVal)) + JS::RootedObject gameResponseObj(rq.cx, gameResponse.toObjectOrNull()); + JS::RootedValue dataVal(rq.cx); + if (!JS_GetProperty(rq.cx, gameResponseObj, "data", &dataVal)) FAIL("data property not in response."); // [{"id": 42, ...}, ...] if (!dataVal.isObject()) FAIL("data property not an object."); - JS::RootedObject data(cx, dataVal.toObjectOrNull()); + JS::RootedObject data(rq.cx, dataVal.toObjectOrNull()); u32 length; bool isArray; - if (!JS_IsArrayObject(cx, data, &isArray) || !isArray || !JS_GetArrayLength(cx, data, &length) || !length) + if (!JS_IsArrayObject(rq.cx, data, &isArray) || !isArray || !JS_GetArrayLength(rq.cx, data, &length) || !length) FAIL("data property not an array with at least one element."); // {"id": 42, ...} - JS::RootedValue first(cx); - if (!JS_GetElement(cx, data, 0, &first)) + JS::RootedValue first(rq.cx); + if (!JS_GetElement(rq.cx, data, 0, &first)) FAIL("Couldn't get first element."); if (!first.isObject()) FAIL("First element not an object."); - JS::RootedObject firstObj(cx, &first.toObject()); + JS::RootedObject firstObj(rq.cx, &first.toObject()); bool hasIdProperty; - if (!JS_HasProperty(cx, firstObj, "id", &hasIdProperty) || !hasIdProperty) + if (!JS_HasProperty(rq.cx, firstObj, "id", &hasIdProperty) || !hasIdProperty) FAIL("No id property in first element."); - JS::RootedValue idProperty(cx); - ENSURE(JS_GetProperty(cx, firstObj, "id", &idProperty)); + JS::RootedValue idProperty(rq.cx); + ENSURE(JS_GetProperty(rq.cx, firstObj, "id", &idProperty)); // Make sure the property is not set to something that could be converted to a bogus value // TODO: We should be able to convert JS::Values to C++ variables in a way that actually @@ -634,7 +633,7 @@ FAIL("id property not a number."); id = -1; - if (!ScriptInterface::FromJSVal(cx, idProperty, id) || id <= 0) + if (!ScriptInterface::FromJSVal(rq, idProperty, id) || id <= 0) FAIL("Invalid id."); return true; @@ -658,10 +657,9 @@ // Make sure we don't end up passing partial results back #define CLEANUP() modData.clear(); - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(scriptInterface); - JS::RootedValue modResponse(cx); + JS::RootedValue modResponse(rq.cx); if (!scriptInterface.ParseJSON(responseData, &modResponse)) FAIL("Failed to parse response as JSON."); @@ -669,19 +667,19 @@ if (!modResponse.isObject()) FAIL("response not an object."); - JS::RootedObject modResponseObj(cx, modResponse.toObjectOrNull()); - JS::RootedValue dataVal(cx); - if (!JS_GetProperty(cx, modResponseObj, "data", &dataVal)) + JS::RootedObject modResponseObj(rq.cx, modResponse.toObjectOrNull()); + JS::RootedValue dataVal(rq.cx); + if (!JS_GetProperty(rq.cx, modResponseObj, "data", &dataVal)) FAIL("data property not in response."); // [modobj1, modobj2, ... ] if (!dataVal.isObject()) FAIL("data property not an object."); - JS::RootedObject rData(cx, dataVal.toObjectOrNull()); + JS::RootedObject rData(rq.cx, dataVal.toObjectOrNull()); u32 length; bool isArray; - if (!JS_IsArrayObject(cx, rData, &isArray) || !isArray || !JS_GetArrayLength(cx, rData, &length) || !length) + if (!JS_IsArrayObject(rq.cx, rData, &isArray) || !isArray || !JS_GetArrayLength(rq.cx, rData, &length) || !length) FAIL("data property not an array with at least one element."); modData.clear(); @@ -698,8 +696,8 @@ { modData.emplace_back(); ModIoModData& data = modData.back(); - JS::RootedValue el(cx); - if (!JS_GetElement(cx, rData, i, &el) || !el.isObject()) + JS::RootedValue el(rq.cx); + if (!JS_GetElement(rq.cx, rData, i, &el) || !el.isObject()) INVALIDATE_DATA_AND_CONTINUE("Failed to get array element object.") bool ok = true; @@ -708,7 +706,7 @@ for (const std::string& prop : { __VA_ARGS__ }) \ { \ std::string val; \ - if (!ScriptInterface::FromJSProperty(cx, obj, prop.c_str(), val, true)) \ + if (!ScriptInterface::FromJSProperty(rq, obj, prop.c_str(), val, true)) \ { \ ok = false; \ copyStringError = "Failed to get " + prop + " from " + #obj + "."; \ @@ -723,9 +721,9 @@ COPY_STRINGS_ELSE_CONTINUE("", el, "name", "name_id", "summary") // Now copy over the modfile part, but without the pointless substructure - JS::RootedObject elObj(cx, el.toObjectOrNull()); - JS::RootedValue modFile(cx); - if (!JS_GetProperty(cx, elObj, "modfile", &modFile)) + JS::RootedObject elObj(rq.cx, el.toObjectOrNull()); + JS::RootedValue modFile(rq.cx); + if (!JS_GetProperty(rq.cx, elObj, "modfile", &modFile)) INVALIDATE_DATA_AND_CONTINUE("Failed to get modfile data."); if (!modFile.isObject()) @@ -733,36 +731,36 @@ COPY_STRINGS_ELSE_CONTINUE("", modFile, "version", "filesize"); - JS::RootedObject modFileObj(cx, modFile.toObjectOrNull()); - JS::RootedValue filehash(cx); - if (!JS_GetProperty(cx, modFileObj, "filehash", &filehash)) + JS::RootedObject modFileObj(rq.cx, modFile.toObjectOrNull()); + JS::RootedValue filehash(rq.cx); + if (!JS_GetProperty(rq.cx, modFileObj, "filehash", &filehash)) INVALIDATE_DATA_AND_CONTINUE("Failed to get filehash data."); COPY_STRINGS_ELSE_CONTINUE("filehash_", filehash, "md5"); - JS::RootedValue download(cx); - if (!JS_GetProperty(cx, modFileObj, "download", &download)) + JS::RootedValue download(rq.cx); + if (!JS_GetProperty(rq.cx, modFileObj, "download", &download)) INVALIDATE_DATA_AND_CONTINUE("Failed to get download data."); COPY_STRINGS_ELSE_CONTINUE("", download, "binary_url"); // Parse metadata_blob (sig+deps) std::string metadata_blob; - if (!ScriptInterface::FromJSProperty(cx, modFile, "metadata_blob", metadata_blob, true)) + if (!ScriptInterface::FromJSProperty(rq, modFile, "metadata_blob", metadata_blob, true)) INVALIDATE_DATA_AND_CONTINUE("Failed to get metadata_blob from modFile."); - JS::RootedValue metadata(cx); + JS::RootedValue metadata(rq.cx); if (!scriptInterface.ParseJSON(metadata_blob, &metadata)) INVALIDATE_DATA_AND_CONTINUE("Failed to parse metadata_blob as JSON."); if (!metadata.isObject()) INVALIDATE_DATA_AND_CONTINUE("metadata_blob is not decoded as an object."); - if (!ScriptInterface::FromJSProperty(cx, metadata, "dependencies", data.dependencies, true)) + if (!ScriptInterface::FromJSProperty(rq, metadata, "dependencies", data.dependencies, true)) INVALIDATE_DATA_AND_CONTINUE("Failed to get dependencies from metadata_blob."); std::vector minisigs; - if (!ScriptInterface::FromJSProperty(cx, metadata, "minisigs", minisigs, true)) + if (!ScriptInterface::FromJSProperty(rq, metadata, "minisigs", minisigs, true)) INVALIDATE_DATA_AND_CONTINUE("Failed to get minisigs from metadata_blob."); // Check we did find a valid matching signature. Index: ps/trunk/source/ps/ProfileViewer.cpp =================================================================== --- ps/trunk/source/ps/ProfileViewer.cpp +++ ps/trunk/source/ps/ProfileViewer.cpp @@ -502,12 +502,11 @@ void operator() (AbstractProfileTable* table) { - JSContext* cx = m_ScriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(m_ScriptInterface); - JS::RootedValue t(cx); + JS::RootedValue t(rq.cx); ScriptInterface::CreateObject( - cx, + rq, &t, "cols", DumpCols(table), "data", DumpRows(table)); @@ -529,24 +528,23 @@ JS::Value DumpRows(AbstractProfileTable* table) { - JSContext* cx = m_ScriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(m_ScriptInterface); - JS::RootedValue data(cx); - ScriptInterface::CreateObject(cx, &data); + JS::RootedValue data(rq.cx); + ScriptInterface::CreateObject(rq, &data); const std::vector& columns = table->GetColumns(); for (size_t r = 0; r < table->GetNumberRows(); ++r) { - JS::RootedValue row(cx); - ScriptInterface::CreateArray(cx, &row); + JS::RootedValue row(rq.cx); + ScriptInterface::CreateArray(rq, &row); m_ScriptInterface.SetProperty(data, table->GetCellText(r, 0).c_str(), row); if (table->GetChild(r)) { - JS::RootedValue childRows(cx, DumpRows(table->GetChild(r))); + JS::RootedValue childRows(rq.cx, DumpRows(table->GetChild(r))); m_ScriptInterface.SetPropertyInt(row, 0, childRows); } Index: ps/trunk/source/ps/Replay.cpp =================================================================== --- ps/trunk/source/ps/Replay.cpp +++ ps/trunk/source/ps/Replay.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -64,15 +64,14 @@ void CReplayLogger::StartGame(JS::MutableHandleValue attribs) { - JSContext* cx = m_ScriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(m_ScriptInterface); // Add timestamp, since the file-modification-date can change m_ScriptInterface.SetProperty(attribs, "timestamp", (double)std::time(nullptr)); // Add engine version and currently loaded mods for sanity checks when replaying m_ScriptInterface.SetProperty(attribs, "engine_version", engine_version); - JS::RootedValue mods(cx, Mod::GetLoadedModsWithVersions(m_ScriptInterface)); + JS::RootedValue mods(rq.cx, Mod::GetLoadedModsWithVersions(m_ScriptInterface)); m_ScriptInterface.SetProperty(attribs, "mods", mods); m_Directory = createDateIndexSubdirectory(VisualReplay::GetDirectoryPath()); @@ -84,8 +83,7 @@ void CReplayLogger::Turn(u32 n, u32 turnLength, std::vector& commands) { - JSContext* cx = m_ScriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(m_ScriptInterface); *m_Stream << "turn " << n << " " << turnLength << "\n"; @@ -114,11 +112,10 @@ } ScriptInterface& scriptInterface = simulation.GetScriptInterface(); - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(scriptInterface); - JS::RootedValue arg(cx); - JS::RootedValue metadata(cx); + JS::RootedValue arg(rq.cx); + JS::RootedValue metadata(rq.cx); cmpGuiInterface->ScriptCall(INVALID_PLAYER, L"GetReplayMetadata", arg, &metadata); const OsPath fileName = g_Game->GetReplayLogger().GetDirectory() / L"metadata.json"; @@ -165,15 +162,14 @@ void CReplayPlayer::CheckReplayMods(const ScriptInterface& scriptInterface, JS::HandleValue attribs) const { - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(scriptInterface); std::vector> replayMods; scriptInterface.GetProperty(attribs, "mods", replayMods); std::vector> enabledMods; - JS::RootedValue enabledModsJS(cx, Mod::GetLoadedModsWithVersions(scriptInterface)); - scriptInterface.FromJSVal(cx, enabledModsJS, enabledMods); + JS::RootedValue enabledModsJS(rq.cx, Mod::GetLoadedModsWithVersions(scriptInterface)); + scriptInterface.FromJSVal(rq, enabledModsJS, enabledMods); CStr warn; if (replayMods.size() != enabledMods.size()) @@ -233,8 +229,7 @@ u32 turnLength = 0; { - JSContext* cx = g_Game->GetSimulation2()->GetScriptInterface().GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(g_Game->GetSimulation2()->GetScriptInterface()); std::string type; while ((*m_Stream >> type).good()) @@ -243,7 +238,7 @@ { std::string line; std::getline(*m_Stream, line); - JS::RootedValue attribs(cx); + JS::RootedValue attribs(rq.cx); ENSURE(g_Game->GetSimulation2()->GetScriptInterface().ParseJSON(line, &attribs)); CheckReplayMods(g_Game->GetSimulation2()->GetScriptInterface(), attribs); @@ -268,10 +263,10 @@ std::string line; std::getline(*m_Stream, line); - JS::RootedValue data(cx); + JS::RootedValue data(rq.cx); g_Game->GetSimulation2()->GetScriptInterface().ParseJSON(line, &data); g_Game->GetSimulation2()->GetScriptInterface().FreezeObject(data, true); - commands.emplace_back(SimulationCommand(player, cx, data)); + commands.emplace_back(SimulationCommand(player, rq.cx, data)); } else if (type == "hash" || type == "hash-quick") { Index: ps/trunk/source/ps/SavedGame.cpp =================================================================== --- ps/trunk/source/ps/SavedGame.cpp +++ ps/trunk/source/ps/SavedGame.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -52,8 +52,7 @@ Status SavedGames::Save(const CStrW& name, const CStrW& description, CSimulation2& simulation, const shared_ptr& guiMetadataClone) { - JSContext* cx = simulation.GetScriptInterface().GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(simulation.GetScriptInterface()); // Determine the filename to save under const VfsPath basenameFormat(L"saves/" + name); @@ -78,13 +77,13 @@ if (!simulation.SerializeState(simStateStream)) WARN_RETURN(ERR::FAIL); - JS::RootedValue initAttributes(cx, simulation.GetInitAttributes()); - JS::RootedValue mods(cx, Mod::GetLoadedModsWithVersions(simulation.GetScriptInterface())); + JS::RootedValue initAttributes(rq.cx, simulation.GetInitAttributes()); + JS::RootedValue mods(rq.cx, Mod::GetLoadedModsWithVersions(simulation.GetScriptInterface())); - JS::RootedValue metadata(cx); + JS::RootedValue metadata(rq.cx); ScriptInterface::CreateObject( - cx, + rq, &metadata, "engine_version", engine_version, "time", static_cast(now), @@ -92,17 +91,17 @@ "mods", mods, "initAttributes", initAttributes); - JS::RootedValue guiMetadata(cx); + JS::RootedValue guiMetadata(rq.cx); simulation.GetScriptInterface().ReadStructuredClone(guiMetadataClone, &guiMetadata); // get some camera data const CVector3D cameraPosition = g_Game->GetView()->GetCameraPosition(); const CVector3D cameraRotation = g_Game->GetView()->GetCameraRotation(); - JS::RootedValue cameraMetadata(cx); + JS::RootedValue cameraMetadata(rq.cx); ScriptInterface::CreateObject( - cx, + rq, &cameraMetadata, "PosX", cameraPosition.X, "PosY", cameraPosition.Y, @@ -174,9 +173,6 @@ void ReadEntry(const VfsPath& pathname, const CFileInfo& fileInfo, PIArchiveFile archiveFile) { - JSContext* cx = m_ScriptInterface.GetContext(); - JSAutoRequest rq(cx); - if (pathname == L"metadata.json") { std::string buffer; @@ -230,11 +226,10 @@ JS::Value SavedGames::GetSavedGames(const ScriptInterface& scriptInterface) { TIMER(L"GetSavedGames"); - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(scriptInterface); - JS::RootedValue games(cx); - ScriptInterface::CreateArray(cx, &games); + JS::RootedValue games(rq.cx); + ScriptInterface::CreateArray(rq, &games); Status err; @@ -267,11 +262,11 @@ DEBUG_WARN_ERR(err); continue; // skip this file } - JS::RootedValue metadata(cx, loader.GetMetadata()); + JS::RootedValue metadata(rq.cx, loader.GetMetadata()); - JS::RootedValue game(cx); + JS::RootedValue game(rq.cx); ScriptInterface::CreateObject( - cx, + rq, &game, "id", pathnames[i].Basename(), "metadata", metadata); Index: ps/trunk/source/ps/VisualReplay.cpp =================================================================== --- ps/trunk/source/ps/VisualReplay.cpp +++ ps/trunk/source/ps/VisualReplay.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -71,9 +71,6 @@ bool VisualReplay::ReadCacheFile(const ScriptInterface& scriptInterface, JS::MutableHandleObject cachedReplaysObject) { - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); - if (!FileExists(GetCacheFilePath())) return false; @@ -81,12 +78,14 @@ CStr cacheStr((std::istreambuf_iterator(cacheStream)), std::istreambuf_iterator()); cacheStream.close(); - JS::RootedValue cachedReplays(cx); + ScriptInterface::Request rq(scriptInterface); + + JS::RootedValue cachedReplays(rq.cx); if (scriptInterface.ParseJSON(cacheStr, &cachedReplays)) { cachedReplaysObject.set(&cachedReplays.toObject()); bool isArray; - if (JS_IsArrayObject(cx, cachedReplaysObject, &isArray) && isArray) + if (JS_IsArrayObject(rq.cx, cachedReplaysObject, &isArray) && isArray) return true; } @@ -97,10 +96,9 @@ void VisualReplay::StoreCacheFile(const ScriptInterface& scriptInterface, JS::HandleObject replays) { - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(scriptInterface); - JS::RootedValue replaysRooted(cx, JS::ObjectValue(*replays)); + JS::RootedValue replaysRooted(rq.cx, JS::ObjectValue(*replays)); std::ofstream cacheStream(OsString(GetTempCacheFilePath()).c_str(), std::ofstream::out | std::ofstream::trunc); cacheStream << scriptInterface.StringifyJSON(&replaysRooted); cacheStream.close(); @@ -113,26 +111,25 @@ JS::HandleObject VisualReplay::ReloadReplayCache(const ScriptInterface& scriptInterface, bool compareFiles) { TIMER(L"ReloadReplayCache"); - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(scriptInterface); // Maps the filename onto the index and size typedef std::map> replayCacheMap; replayCacheMap fileList; - JS::RootedObject cachedReplaysObject(cx); + JS::RootedObject cachedReplaysObject(rq.cx); if (ReadCacheFile(scriptInterface, &cachedReplaysObject)) { // Create list of files included in the cache u32 cacheLength = 0; - JS_GetArrayLength(cx, cachedReplaysObject, &cacheLength); + JS_GetArrayLength(rq.cx, cachedReplaysObject, &cacheLength); for (u32 j = 0; j < cacheLength; ++j) { - JS::RootedValue replay(cx); - JS_GetElement(cx, cachedReplaysObject, j, &replay); + JS::RootedValue replay(rq.cx); + JS_GetElement(rq.cx, cachedReplaysObject, j, &replay); - JS::RootedValue file(cx); + JS::RootedValue file(rq.cx); OsPath fileName; double fileSize; scriptInterface.GetProperty(replay, "directory", fileName); @@ -142,7 +139,7 @@ } } - JS::RootedObject replays(cx, JS_NewArrayObject(cx, 0)); + JS::RootedObject replays(rq.cx, JS_NewArrayObject(rq.cx, 0)); DirectoryNames directories; if (GetDirectoryEntries(GetDirectoryPath(), nullptr, &directories) != INFO::OK) @@ -182,7 +179,7 @@ if (isNew) { - JS::RootedValue replayData(cx, LoadReplayData(scriptInterface, directory)); + JS::RootedValue replayData(rq.cx, LoadReplayData(scriptInterface, directory)); if (replayData.isNull()) { if (!FileExists(replayFile)) @@ -191,12 +188,12 @@ GetFileInfo(replayFile, &fileInfo); ScriptInterface::CreateObject( - cx, + rq, &replayData, "directory", directory.string(), "fileSize", static_cast(fileInfo.Size())); } - JS_SetElement(cx, replays, i++, replayData); + JS_SetElement(rq.cx, replays, i++, replayData); newReplays = true; } else @@ -219,9 +216,9 @@ if (!copyFromOldCache.empty()) for (u32 j : copyFromOldCache) { - JS::RootedValue replay(cx); - JS_GetElement(cx, cachedReplaysObject, j, &replay); - JS_SetElement(cx, replays, i++, replay); + JS::RootedValue replay(rq.cx); + JS_GetElement(rq.cx, cachedReplaysObject, j, &replay); + JS_SetElement(rq.cx, replays, i++, replay); } } StoreCacheFile(scriptInterface, replays); @@ -231,19 +228,19 @@ JS::Value VisualReplay::GetReplays(const ScriptInterface& scriptInterface, bool compareFiles) { TIMER(L"GetReplays"); - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); - JS::RootedObject replays(cx, ReloadReplayCache(scriptInterface, compareFiles)); + + ScriptInterface::Request rq(scriptInterface); + JS::RootedObject replays(rq.cx, ReloadReplayCache(scriptInterface, compareFiles)); // Only take entries with data - JS::RootedValue replaysWithoutNullEntries(cx); - ScriptInterface::CreateArray(cx, &replaysWithoutNullEntries); + JS::RootedValue replaysWithoutNullEntries(rq.cx); + ScriptInterface::CreateArray(rq, &replaysWithoutNullEntries); u32 replaysLength = 0; - JS_GetArrayLength(cx, replays, &replaysLength); + JS_GetArrayLength(rq.cx, replays, &replaysLength); for (u32 j = 0, i = 0; j < replaysLength; ++j) { - JS::RootedValue replay(cx); - JS_GetElement(cx, replays, j, &replay); + JS::RootedValue replay(rq.cx); + JS_GetElement(rq.cx, replays, j, &replay); if (scriptInterface.HasProperty(replay, "attribs")) scriptInterface.SetPropertyInt(replaysWithoutNullEntries, i++, replay); } @@ -369,9 +366,8 @@ // Parse header / first line CStr header; std::getline(*replayStream, header); - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); - JS::RootedValue attribs(cx); + ScriptInterface::Request rq(scriptInterface); + JS::RootedValue attribs(rq.cx); if (!scriptInterface.ParseJSON(header, &attribs)) { LOGERROR("Couldn't parse replay header of %s", replayFile.string8().c_str()); @@ -404,10 +400,10 @@ return JS::NullValue(); // Return the actual data - JS::RootedValue replayData(cx); + JS::RootedValue replayData(rq.cx); ScriptInterface::CreateObject( - cx, + rq, &replayData, "directory", directory.string(), "fileSize", static_cast(fileSize), @@ -430,10 +426,9 @@ JS::Value VisualReplay::GetReplayAttributes(ScriptInterface::CxPrivate* pCxPrivate, const OsPath& directoryName) { // Create empty JS object - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); - JS::RootedValue attribs(cx); - ScriptInterface::CreateObject(cx, &attribs); + ScriptInterface::Request rq(pCxPrivate); + JS::RootedValue attribs(rq.cx); + ScriptInterface::CreateObject(rq, &attribs); // Return empty object if file doesn't exist const OsPath replayFile = GetDirectoryPath() / directoryName / L"commands.txt"; @@ -455,20 +450,19 @@ void VisualReplay::AddReplayToCache(const ScriptInterface& scriptInterface, const CStrW& directoryName) { TIMER(L"AddReplayToCache"); - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(scriptInterface); - JS::RootedValue replayData(cx, LoadReplayData(scriptInterface, OsPath(directoryName))); + JS::RootedValue replayData(rq.cx, LoadReplayData(scriptInterface, OsPath(directoryName))); if (replayData.isNull()) return; - JS::RootedObject cachedReplaysObject(cx); + JS::RootedObject cachedReplaysObject(rq.cx); if (!ReadCacheFile(scriptInterface, &cachedReplaysObject)) - cachedReplaysObject = JS_NewArrayObject(cx, 0); + cachedReplaysObject = JS_NewArrayObject(rq.cx, 0); u32 cacheLength = 0; - JS_GetArrayLength(cx, cachedReplaysObject, &cacheLength); - JS_SetElement(cx, cachedReplaysObject, cacheLength, replayData); + JS_GetArrayLength(rq.cx, cachedReplaysObject, &cacheLength); + JS_SetElement(rq.cx, cachedReplaysObject, cacheLength, replayData); StoreCacheFile(scriptInterface, cachedReplaysObject); } @@ -491,9 +485,8 @@ if (!HasReplayMetadata(directoryName)) return JS::NullValue(); - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); - JS::RootedValue metadata(cx); + ScriptInterface::Request rq(pCxPrivate); + JS::RootedValue metadata(rq.cx); std::ifstream* stream = new std::ifstream(OsString(GetDirectoryPath() / directoryName / L"metadata.json").c_str()); ENSURE(stream->good()); Index: ps/trunk/source/ps/scripting/JSInterface_Game.cpp =================================================================== --- ps/trunk/source/ps/scripting/JSInterface_Game.cpp +++ ps/trunk/source/ps/scripting/JSInterface_Game.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -48,10 +48,9 @@ // Convert from GUI script context to sim script context CSimulation2* sim = g_Game->GetSimulation2(); - JSContext* cxSim = sim->GetScriptInterface().GetContext(); - JSAutoRequest rqSim(cxSim); + ScriptInterface::Request rqSim(sim->GetScriptInterface()); - JS::RootedValue gameAttribs(cxSim, + JS::RootedValue gameAttribs(rqSim.cx, sim->GetScriptInterface().CloneValueFromOtherContext(*(pCxPrivate->pScriptInterface), attribs)); g_Game->SetPlayerID(playerID); @@ -101,9 +100,8 @@ { if (!g_Game) { - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); - JS_ReportError(cx, "Game is not started"); + ScriptInterface::Request rq(pCxPrivate); + JS_ReportError(rq.cx, "Game is not started"); return false; } @@ -114,9 +112,8 @@ { if (!g_Game) { - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); - JS_ReportError(cx, "Game is not started"); + ScriptInterface::Request rq(pCxPrivate); + JS_ReportError(rq.cx, "Game is not started"); return; } Index: ps/trunk/source/ps/scripting/JSInterface_Main.cpp =================================================================== --- ps/trunk/source/ps/scripting/JSInterface_Main.cpp +++ ps/trunk/source/ps/scripting/JSInterface_Main.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2018 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -73,15 +73,14 @@ JS::Value JSI_Main::LoadMapSettings(ScriptInterface::CxPrivate* pCxPrivate, const VfsPath& pathname) { - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(pCxPrivate); CMapSummaryReader reader; if (reader.LoadMap(pathname) != PSRETURN_OK) return JS::UndefinedValue(); - JS::RootedValue settings(cx); + JS::RootedValue settings(rq.cx); reader.GetMapSettings(*(pCxPrivate->pScriptInterface), &settings); return settings; } Index: ps/trunk/source/ps/scripting/JSInterface_ModIo.cpp =================================================================== --- ps/trunk/source/ps/scripting/JSInterface_ModIo.cpp +++ ps/trunk/source/ps/scripting/JSInterface_ModIo.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -86,19 +86,18 @@ } ScriptInterface* scriptInterface = pCxPrivate->pScriptInterface; - JSContext* cx = scriptInterface->GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(scriptInterface); const std::vector& availableMods = g_ModIo->GetMods(); - JS::RootedValue mods(cx); - ScriptInterface::CreateArray(cx, &mods, availableMods.size()); + JS::RootedValue mods(rq.cx); + ScriptInterface::CreateArray(rq, &mods, availableMods.size()); u32 i = 0; for (const ModIoModData& mod : availableMods) { - JS::RootedValue m(cx); - ScriptInterface::CreateObject(cx, &m); + JS::RootedValue m(rq.cx); + ScriptInterface::CreateObject(rq, &m); for (const std::pair& prop : mod.properties) scriptInterface->SetProperty(m, prop.first.c_str(), prop.second, true); @@ -133,13 +132,12 @@ } ScriptInterface* scriptInterface = pCxPrivate->pScriptInterface; - JSContext* cx = scriptInterface->GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(scriptInterface); const DownloadProgressData& progress = g_ModIo->GetDownloadProgress(); - JS::RootedValue progressData(cx); - ScriptInterface::CreateObject(cx, &progressData); + JS::RootedValue progressData(rq.cx); + ScriptInterface::CreateObject(rq, &progressData); scriptInterface->SetProperty(progressData, "status", statusStrings.at(progress.status), true); scriptInterface->SetProperty(progressData, "progress", progress.progress, true); scriptInterface->SetProperty(progressData, "error", progress.error, true); Index: ps/trunk/source/ps/scripting/JSInterface_SavedGame.cpp =================================================================== --- ps/trunk/source/ps/scripting/JSInterface_SavedGame.cpp +++ ps/trunk/source/ps/scripting/JSInterface_SavedGame.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -78,8 +78,7 @@ // The GUI calls this function from the GUI context and expects the return value in the same context. // The game we start from here creates another context and expects data in this context. - JSContext* cxGui = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cxGui); + ScriptInterface::Request rqGui(pCxPrivate); ENSURE(!g_NetServer); ENSURE(!g_NetClient); @@ -87,7 +86,7 @@ ENSURE(!g_Game); // Load the saved game data from disk - JS::RootedValue guiContextMetadata(cxGui); + JS::RootedValue guiContextMetadata(rqGui.cx); std::string savedState; Status err = SavedGames::Load(name, *(pCxPrivate->pScriptInterface), &guiContextMetadata, savedState); if (err < 0) @@ -97,12 +96,11 @@ { CSimulation2* sim = g_Game->GetSimulation2(); - JSContext* cxGame = sim->GetScriptInterface().GetContext(); - JSAutoRequest rq(cxGame); + ScriptInterface::Request rqGame(sim->GetScriptInterface()); - JS::RootedValue gameContextMetadata(cxGame, + JS::RootedValue gameContextMetadata(rqGame.cx, sim->GetScriptInterface().CloneValueFromOtherContext(*(pCxPrivate->pScriptInterface), guiContextMetadata)); - JS::RootedValue gameInitAttributes(cxGame); + JS::RootedValue gameInitAttributes(rqGame.cx); sim->GetScriptInterface().GetProperty(gameContextMetadata, "initAttributes", &gameInitAttributes); int playerID; Index: ps/trunk/source/ps/scripting/JSInterface_VFS.cpp =================================================================== --- ps/trunk/source/ps/scripting/JSInterface_VFS.cpp +++ ps/trunk/source/ps/scripting/JSInterface_VFS.cpp @@ -48,15 +48,17 @@ // state held across multiple BuildDirEntListCB calls; init by BuildDirEntList. struct BuildDirEntListState { - JSContext* cx; + ScriptInterface* pScriptInterface; JS::PersistentRootedObject filename_array; int cur_idx; - BuildDirEntListState(JSContext* cx_) - : cx(cx_), - filename_array(cx, JS_NewArrayObject(cx, JS::HandleValueArray::empty())), + BuildDirEntListState(ScriptInterface* scriptInterface) + : pScriptInterface(scriptInterface), + filename_array(scriptInterface->GetJSRuntime()), cur_idx(0) { + ScriptInterface::Request rq(pScriptInterface); + filename_array = JS_NewArrayObject(rq.cx, JS::HandleValueArray::empty()); } }; @@ -64,12 +66,12 @@ static Status BuildDirEntListCB(const VfsPath& pathname, const CFileInfo& UNUSED(fileINfo), uintptr_t cbData) { BuildDirEntListState* s = (BuildDirEntListState*)cbData; - JSAutoRequest rq(s->cx); + ScriptInterface::Request rq(s->pScriptInterface); - JS::RootedObject filenameArrayObj(s->cx, s->filename_array); - JS::RootedValue val(s->cx); - ScriptInterface::ToJSVal( s->cx, &val, CStrW(pathname.string()) ); - JS_SetElement(s->cx, filenameArrayObj, s->cur_idx++, val); + JS::RootedObject filenameArrayObj(rq.cx, s->filename_array); + JS::RootedValue val(rq.cx); + ScriptInterface::ToJSVal(rq, &val, CStrW(pathname.string()) ); + JS_SetElement(rq.cx, filenameArrayObj, s->cur_idx++, val); return INFO::OK; } @@ -91,11 +93,8 @@ int flags = recurse ? vfs::DIR_RECURSIVE : 0; - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); - // build array in the callback function - BuildDirEntListState state(cx); + BuildDirEntListState state(pCxPrivate->pScriptInterface); vfs::ForEachFile(g_VFS, path, BuildDirEntListCB, (uintptr_t)&state, filter, flags); return JS::ObjectValue(*state.filename_array); @@ -130,9 +129,6 @@ // Return file contents in a string. Assume file is UTF-8 encoded text. JS::Value JSI_VFS::ReadFile(ScriptInterface::CxPrivate* pCxPrivate, const std::wstring& filename) { - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); - CVFSFile file; if (file.Load(g_VFS, filename) != PSRETURN_OK) return JS::NullValue(); @@ -143,18 +139,15 @@ contents.Replace("\r\n", "\n"); // Decode as UTF-8 - JS::RootedValue ret(cx); - ScriptInterface::ToJSVal(cx, &ret, contents.FromUTF8()); + ScriptInterface::Request rq(pCxPrivate); + JS::RootedValue ret(rq.cx); + ScriptInterface::ToJSVal(rq, &ret, contents.FromUTF8()); return ret; } // Return file contents as an array of lines. Assume file is UTF-8 encoded text. JS::Value JSI_VFS::ReadFileLines(ScriptInterface::CxPrivate* pCxPrivate, const std::wstring& filename) { - const ScriptInterface& scriptInterface = *pCxPrivate->pScriptInterface; - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); - CVFSFile file; if (file.Load(g_VFS, filename) != PSRETURN_OK) return JS::NullValue(); @@ -167,8 +160,11 @@ // split into array of strings (one per line) std::stringstream ss(contents); - JS::RootedValue line_array(cx); - ScriptInterface::CreateArray(cx, &line_array); + const ScriptInterface& scriptInterface = *pCxPrivate->pScriptInterface; + ScriptInterface::Request rq(scriptInterface); + + JS::RootedValue line_array(rq.cx); + ScriptInterface::CreateArray(rq, &line_array); std::string line; int cur_line = 0; @@ -176,8 +172,8 @@ while (std::getline(ss, line)) { // Decode each line as UTF-8 - JS::RootedValue val(cx); - ScriptInterface::ToJSVal(cx, &val, CStr(line).FromUTF8()); + JS::RootedValue val(rq.cx); + ScriptInterface::ToJSVal(rq, &val, CStr(line).FromUTF8()); scriptInterface.SetPropertyInt(line_array, cur_line++, val); } @@ -189,22 +185,22 @@ if (!PathRestrictionMet(pCxPrivate, validPaths, filePath)) return JS::NullValue(); - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); - JS::RootedValue out(cx); - pCxPrivate->pScriptInterface->ReadJSONFile(filePath, &out); + const ScriptInterface& scriptInterface = *pCxPrivate->pScriptInterface; + ScriptInterface::Request rq(scriptInterface); + JS::RootedValue out(rq.cx); + scriptInterface.ReadJSONFile(filePath, &out); return out; } void JSI_VFS::WriteJSONFile(ScriptInterface::CxPrivate* pCxPrivate, const std::wstring& filePath, JS::HandleValue val1) { - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); + const ScriptInterface& scriptInterface = *pCxPrivate->pScriptInterface; + ScriptInterface::Request rq(scriptInterface); // TODO: This is a workaround because we need to pass a MutableHandle to StringifyJSON. - JS::RootedValue val(cx, val1); + JS::RootedValue val(rq.cx, val1); - std::string str(pCxPrivate->pScriptInterface->StringifyJSON(&val, false)); + std::string str(scriptInterface.StringifyJSON(&val, false)); VfsPath path(filePath); WriteBuffer buf; @@ -227,9 +223,8 @@ allowedPaths += L"\"" + validPaths[i] + L"\""; } - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); - JS_ReportError(cx, "This part of the engine may only read from %s!", utf8_from_wstring(allowedPaths).c_str()); + ScriptInterface::Request rq(pCxPrivate); + JS_ReportError(rq.cx, "This part of the engine may only read from %s!", utf8_from_wstring(allowedPaths).c_str()); return false; } Index: ps/trunk/source/rlinterface/RLInterface.cpp =================================================================== --- ps/trunk/source/rlinterface/RLInterface.cpp +++ ps/trunk/source/rlinterface/RLInterface.cpp @@ -298,9 +298,9 @@ g_Game = new CGame(m_ScenarioConfig.saveReplay); ScriptInterface& scriptInterface = g_Game->GetSimulation2()->GetScriptInterface(); - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); - JS::RootedValue attrs(cx); + ScriptInterface::Request rq(scriptInterface); + + JS::RootedValue attrs(rq.cx); scriptInterface.ParseJSON(m_ScenarioConfig.content, &attrs); g_Game->SetPlayerID(m_ScenarioConfig.playerID); @@ -316,12 +316,12 @@ } else { - JS::RootedValue initData(cx); - scriptInterface.CreateObject(cx, &initData); + JS::RootedValue initData(rq.cx); + scriptInterface.CreateObject(rq, &initData); scriptInterface.SetProperty(initData, "attribs", attrs); - JS::RootedValue playerAssignments(cx); - scriptInterface.CreateObject(cx, &playerAssignments); + JS::RootedValue playerAssignments(rq.cx); + scriptInterface.CreateObject(rq, &playerAssignments); scriptInterface.SetProperty(initData, "playerAssignments", playerAssignments); g_GUI->SwitchPage(L"page_loading.xml", &scriptInterface, initData); @@ -339,14 +339,14 @@ m_msgLock.unlock(); return; } - const ScriptInterface& scriptInterface = g_Game->GetSimulation2()->GetScriptInterface(); + CLocalTurnManager* turnMgr = static_cast(g_Game->GetTurnManager()); + const ScriptInterface& scriptInterface = g_Game->GetSimulation2()->GetScriptInterface(); + ScriptInterface::Request rq(scriptInterface); for (Command command : msg.commands) { - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); - JS::RootedValue commandJSON(cx); + JS::RootedValue commandJSON(rq.cx); scriptInterface.ParseJSON(command.json_cmd, &commandJSON); turnMgr->PostCommand(command.playerID, commandJSON); } @@ -376,11 +376,11 @@ std::string RLInterface::GetGameState() { const ScriptInterface& scriptInterface = g_Game->GetSimulation2()->GetScriptInterface(); + ScriptInterface::Request rq(scriptInterface); + const CSimContext simContext = g_Game->GetSimulation2()->GetSimContext(); CmpPtr cmpAIInterface(simContext.GetSystemEntity()); - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); - JS::RootedValue state(cx); + JS::RootedValue state(rq.cx); cmpAIInterface->GetFullRepresentation(&state, true); return scriptInterface.StringifyJSON(&state, false); } Index: ps/trunk/source/scriptinterface/NativeWrapperDecls.h =================================================================== --- ps/trunk/source/scriptinterface/NativeWrapperDecls.h +++ ps/trunk/source/scriptinterface/NativeWrapperDecls.h @@ -43,7 +43,7 @@ #define CONVERT_ARG(z, i, data) \ bool typeConvRet##i; \ T##i a##i = ScriptInterface::AssignOrFromJSVal( \ - cx, \ + rq, \ i < args.length() ? args[i] : JS::UndefinedHandleValue, \ typeConvRet##i); \ if (!typeConvRet##i) return false; Index: ps/trunk/source/scriptinterface/NativeWrapperDefns.h =================================================================== --- ps/trunk/source/scriptinterface/NativeWrapperDefns.h +++ ps/trunk/source/scriptinterface/NativeWrapperDefns.h @@ -61,7 +61,7 @@ // (NativeWrapperDecls.h set up a lot of the macros we use here) -// ScriptInterface_NativeWrapper::call(cx, rval, fptr, args...) will call fptr(cbdata, args...), +// ScriptInterface_NativeWrapper::call(rq, rval, fptr, args...) will call fptr(cbdata, args...), // and if T != void then it will store the result in rval: // Templated on the return type so void can be handled separately @@ -69,9 +69,9 @@ struct ScriptInterface_NativeWrapper { template - static void call(JSContext* cx, JS::MutableHandleValue rval, F fptr, Ts... params) + static void call(const ScriptInterface::Request& rq, JS::MutableHandleValue rval, F fptr, Ts... params) { - ScriptInterface::AssignOrToJSValUnrooted(cx, rval, fptr(ScriptInterface::GetScriptInterfaceAndCBData(cx), params...)); + ScriptInterface::AssignOrToJSValUnrooted(rq, rval, fptr(ScriptInterface::GetScriptInterfaceAndCBData(rq.cx), params...)); } }; @@ -80,9 +80,9 @@ struct ScriptInterface_NativeWrapper { template - static void call(JSContext* cx, JS::MutableHandleValue UNUSED(rval), F fptr, Ts... params) + static void call(const ScriptInterface::Request& rq, JS::MutableHandleValue UNUSED(rval), F fptr, Ts... params) { - fptr(ScriptInterface::GetScriptInterfaceAndCBData(cx), params...); + fptr(ScriptInterface::GetScriptInterfaceAndCBData(rq.cx), params...); } }; @@ -92,9 +92,9 @@ struct ScriptInterface_NativeMethodWrapper { template - static void call(JSContext* cx, JS::MutableHandleValue rval, TC* c, F fptr, Ts... params) + static void call(const ScriptInterface::Request& rq, JS::MutableHandleValue rval, TC* c, F fptr, Ts... params) { - ScriptInterface::AssignOrToJSValUnrooted(cx, rval, (c->*fptr)(params...)); + ScriptInterface::AssignOrToJSValUnrooted(rq, rval, (c->*fptr)(params...)); } }; @@ -102,7 +102,7 @@ struct ScriptInterface_NativeMethodWrapper { template - static void call(JSContext* UNUSED(cx), JS::MutableHandleValue UNUSED(rval), TC* c, F fptr, Ts... params) + static void call(const ScriptInterface::Request& UNUSED(rq), JS::MutableHandleValue UNUSED(rval), TC* c, F fptr, Ts... params) { (c->*fptr)(params...); } @@ -114,12 +114,12 @@ bool ScriptInterface::call(JSContext* cx, uint argc, JS::Value* vp) \ { \ JS::CallArgs args = JS::CallArgsFromVp(argc, vp); \ - JSAutoRequest rq(cx); \ + ScriptInterface::Request rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); \ BOOST_PP_REPEAT_##z (i, CONVERT_ARG, ~) \ - JS::RootedValue rval(cx); \ - ScriptInterface_NativeWrapper::template call(cx, &rval, fptr A0_TAIL(z,i)); \ + JS::RootedValue rval(rq.cx); \ + ScriptInterface_NativeWrapper::template call(rq, &rval, fptr A0_TAIL(z,i)); \ args.rval().set(rval); \ - return !ScriptInterface::IsExceptionPending(cx); \ + return !ScriptInterface::IsExceptionPending(rq); \ } BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~) #undef OVERLOADS @@ -130,14 +130,14 @@ bool ScriptInterface::callMethod(JSContext* cx, uint argc, JS::Value* vp) \ { \ JS::CallArgs args = JS::CallArgsFromVp(argc, vp); \ - JSAutoRequest rq(cx); \ - TC* c = ScriptInterface::GetPrivate(cx, args, CLS); \ + ScriptInterface::Request rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); \ + TC* c = ScriptInterface::GetPrivate(rq, args, CLS); \ if (! c) return false; \ BOOST_PP_REPEAT_##z (i, CONVERT_ARG, ~) \ - JS::RootedValue rval(cx); \ - ScriptInterface_NativeMethodWrapper::template call(cx, &rval, c, fptr A0_TAIL(z,i)); \ + JS::RootedValue rval(rq.cx); \ + ScriptInterface_NativeMethodWrapper::template call(rq, &rval, c, fptr A0_TAIL(z,i)); \ args.rval().set(rval); \ - return !ScriptInterface::IsExceptionPending(cx); \ + return !ScriptInterface::IsExceptionPending(rq); \ } BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~) #undef OVERLOADS @@ -148,27 +148,27 @@ bool ScriptInterface::callMethodConst(JSContext* cx, uint argc, JS::Value* vp) \ { \ JS::CallArgs args = JS::CallArgsFromVp(argc, vp); \ - JSAutoRequest rq(cx); \ - TC* c = ScriptInterface::GetPrivate(cx, args, CLS); \ + ScriptInterface::Request rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); \ + TC* c = ScriptInterface::GetPrivate(rq, args, CLS); \ if (! c) return false; \ BOOST_PP_REPEAT_##z (i, CONVERT_ARG, ~) \ - JS::RootedValue rval(cx); \ - ScriptInterface_NativeMethodWrapper::template call(cx, &rval, c, fptr A0_TAIL(z,i)); \ + JS::RootedValue rval(rq.cx); \ + ScriptInterface_NativeMethodWrapper::template call(rq, &rval, c, fptr A0_TAIL(z,i)); \ args.rval().set(rval); \ - return !ScriptInterface::IsExceptionPending(cx); \ + return !ScriptInterface::IsExceptionPending(rq); \ } BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~) #undef OVERLOADS template -static void AssignOrToJSValHelper(JSContext* cx, JS::AutoValueVector& argv, const T& a, const Ts&... params) +static void AssignOrToJSValHelper(const ScriptInterface::Request& rq, JS::AutoValueVector& argv, const T& a, const Ts&... params) { - ScriptInterface::AssignOrToJSVal(cx, argv[i], a); - AssignOrToJSValHelper(cx, argv, params...); + ScriptInterface::AssignOrToJSVal(rq, argv[i], a); + AssignOrToJSValHelper(rq, argv, params...); } template -static void AssignOrToJSValHelper(JSContext* UNUSED(cx), JS::AutoValueVector& UNUSED(argv)) +static void AssignOrToJSValHelper(const ScriptInterface::Request& UNUSED(rq), JS::AutoValueVector& UNUSED(argv)) { cassert(sizeof...(Ts) == 0); // Nop, for terminating the template recursion. @@ -177,37 +177,34 @@ template bool ScriptInterface::CallFunction(JS::HandleValue val, const char* name, R& ret, const Ts&... params) const { - JSContext* cx = GetContext(); - JSAutoRequest rq(cx); - JS::RootedValue jsRet(cx); - JS::AutoValueVector argv(cx); + ScriptInterface::Request rq(this); + JS::RootedValue jsRet(rq.cx); + JS::AutoValueVector argv(rq.cx); argv.resize(sizeof...(Ts)); - AssignOrToJSValHelper<0>(cx, argv, params...); + AssignOrToJSValHelper<0>(rq, argv, params...); if (!CallFunction_(val, name, argv, &jsRet)) return false; - return FromJSVal(cx, jsRet, ret); + return FromJSVal(rq, jsRet, ret); } template bool ScriptInterface::CallFunction(JS::HandleValue val, const char* name, JS::Rooted* ret, const Ts&... params) const { - JSContext* cx = GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(this); JS::MutableHandle jsRet(ret); - JS::AutoValueVector argv(cx); + JS::AutoValueVector argv(rq.cx); argv.resize(sizeof...(Ts)); - AssignOrToJSValHelper<0>(cx, argv, params...); + AssignOrToJSValHelper<0>(rq, argv, params...); return CallFunction_(val, name, argv, jsRet); } template bool ScriptInterface::CallFunction(JS::HandleValue val, const char* name, JS::MutableHandle ret, const Ts&... params) const { - JSContext* cx = GetContext(); - JSAutoRequest rq(cx); - JS::AutoValueVector argv(cx); + ScriptInterface::Request rq(this); + JS::AutoValueVector argv(rq.cx); argv.resize(sizeof...(Ts)); - AssignOrToJSValHelper<0>(cx, argv, params...); + AssignOrToJSValHelper<0>(rq, argv, params...); return CallFunction_(val, name, argv, ret); } @@ -215,12 +212,11 @@ template bool ScriptInterface::CallFunctionVoid(JS::HandleValue val, const char* name, const Ts&... params) const { - JSContext* cx = GetContext(); - JSAutoRequest rq(cx); - JS::RootedValue jsRet(cx); - JS::AutoValueVector argv(cx); + ScriptInterface::Request rq(this); + JS::RootedValue jsRet(rq.cx); + JS::AutoValueVector argv(rq.cx); argv.resize(sizeof...(Ts)); - AssignOrToJSValHelper<0>(cx, argv, params...); + AssignOrToJSValHelper<0>(rq, argv, params...); return CallFunction_(val, name, argv, &jsRet); } Index: ps/trunk/source/scriptinterface/ScriptConversions.h =================================================================== --- ps/trunk/source/scriptinterface/ScriptConversions.h +++ ps/trunk/source/scriptinterface/ScriptConversions.h @@ -23,10 +23,9 @@ #include -template static void ToJSVal_vector(JSContext* cx, JS::MutableHandleValue ret, const std::vector& val) +template static void ToJSVal_vector(const ScriptInterface::Request& rq, JS::MutableHandleValue ret, const std::vector& val) { - JSAutoRequest rq(cx); - JS::RootedObject obj(cx, JS_NewArrayObject(cx, 0)); + JS::RootedObject obj(rq.cx, JS_NewArrayObject(rq.cx, 0)); if (!obj) { ret.setUndefined(); @@ -36,39 +35,38 @@ ENSURE(val.size() <= std::numeric_limits::max()); for (u32 i = 0; i < val.size(); ++i) { - JS::RootedValue el(cx); - ScriptInterface::ToJSVal(cx, &el, val[i]); - JS_SetElement(cx, obj, i, el); + JS::RootedValue el(rq.cx); + ScriptInterface::ToJSVal(rq, &el, val[i]); + JS_SetElement(rq.cx, obj, i, el); } ret.setObject(*obj); } -#define FAIL(msg) STMT(JS_ReportError(cx, msg); return false) +#define FAIL(msg) STMT(JS_ReportError(rq.cx, msg); return false) -template static bool FromJSVal_vector(JSContext* cx, JS::HandleValue v, std::vector& out) +template static bool FromJSVal_vector(const ScriptInterface::Request& rq, JS::HandleValue v, std::vector& out) { - JSAutoRequest rq(cx); - JS::RootedObject obj(cx); + JS::RootedObject obj(rq.cx); if (!v.isObject()) FAIL("Argument must be an array"); bool isArray; obj = &v.toObject(); - if ((!JS_IsArrayObject(cx, obj, &isArray) || !isArray) && !JS_IsTypedArrayObject(obj)) + if ((!JS_IsArrayObject(rq.cx, obj, &isArray) || !isArray) && !JS_IsTypedArrayObject(obj)) FAIL("Argument must be an array"); u32 length; - if (!JS_GetArrayLength(cx, obj, &length)) + if (!JS_GetArrayLength(rq.cx, obj, &length)) FAIL("Failed to get array length"); out.reserve(length); for (u32 i = 0; i < length; ++i) { - JS::RootedValue el(cx); - if (!JS_GetElement(cx, obj, i, &el)) + JS::RootedValue el(rq.cx); + if (!JS_GetElement(rq.cx, obj, i, &el)) FAIL("Failed to read array element"); T el2; - if (!ScriptInterface::FromJSVal(cx, el, el2)) + if (!ScriptInterface::FromJSVal(rq, el, el2)) return false; out.push_back(el2); } @@ -78,35 +76,34 @@ #undef FAIL #define JSVAL_VECTOR(T) \ -template<> void ScriptInterface::ToJSVal >(JSContext* cx, JS::MutableHandleValue ret, const std::vector& val) \ +template<> void ScriptInterface::ToJSVal >(const ScriptInterface::Request& rq, JS::MutableHandleValue ret, const std::vector& val) \ { \ - ToJSVal_vector(cx, ret, val); \ + ToJSVal_vector(rq, ret, val); \ } \ -template<> bool ScriptInterface::FromJSVal >(JSContext* cx, JS::HandleValue v, std::vector& out) \ +template<> bool ScriptInterface::FromJSVal >(const ScriptInterface::Request& rq, JS::HandleValue v, std::vector& out) \ { \ - return FromJSVal_vector(cx, v, out); \ + return FromJSVal_vector(rq, v, out); \ } -template bool ScriptInterface::FromJSProperty(JSContext* cx, const JS::HandleValue val, const char* name, T& ret, bool strict) +template bool ScriptInterface::FromJSProperty(const ScriptInterface::Request& rq, const JS::HandleValue val, const char* name, T& ret, bool strict) { if (!val.isObject()) return false; - JSAutoRequest rq(cx); - JS::RootedObject obj(cx, &val.toObject()); + JS::RootedObject obj(rq.cx, &val.toObject()); bool hasProperty; - if (!JS_HasProperty(cx, obj, name, &hasProperty) || !hasProperty) + if (!JS_HasProperty(rq.cx, obj, name, &hasProperty) || !hasProperty) return false; - JS::RootedValue value(cx); - if (!JS_GetProperty(cx, obj, name, &value)) + JS::RootedValue value(rq.cx); + if (!JS_GetProperty(rq.cx, obj, name, &value)) return false; if (strict && value.isNull()) return false; - return FromJSVal(cx, value, ret); + return FromJSVal(rq, value, ret); } #endif //INCLUDED_SCRIPTCONVERSIONS Index: ps/trunk/source/scriptinterface/ScriptConversions.cpp =================================================================== --- ps/trunk/source/scriptinterface/ScriptConversions.cpp +++ ps/trunk/source/scriptinterface/ScriptConversions.cpp @@ -24,10 +24,10 @@ #include "ps/utf16string.h" #include "ps/CStr.h" -#define FAIL(msg) STMT(JS_ReportError(cx, msg); return false) +#define FAIL(msg) STMT(JS_ReportError(rq.cx, msg); return false) // Implicit type conversions often hide bugs, so warn about them -#define WARN_IF_NOT(c, v) STMT(if (!(c)) { JS_ReportWarning(cx, "Script value conversion check failed: %s (got type %s)", #c, InformalValueTypeName(v)); }) +#define WARN_IF_NOT(c, v) STMT(if (!(c)) { JS_ReportWarning(rq.cx, "Script value conversion check failed: %s (got type %s)", #c, InformalValueTypeName(v)); }) // TODO: SpiderMonkey: Follow upstream progresses about JS_InformalValueTypeName in the API // https://bugzilla.mozilla.org/show_bug.cgi?id=1285917 @@ -50,77 +50,69 @@ return "value"; } -template<> bool ScriptInterface::FromJSVal(JSContext* cx, JS::HandleValue v, bool& out) +template<> bool ScriptInterface::FromJSVal(const Request& rq, JS::HandleValue v, bool& out) { - JSAutoRequest rq(cx); WARN_IF_NOT(v.isBoolean(), v); out = JS::ToBoolean(v); return true; } -template<> bool ScriptInterface::FromJSVal(JSContext* cx, JS::HandleValue v, float& out) +template<> bool ScriptInterface::FromJSVal(const Request& rq, JS::HandleValue v, float& out) { - JSAutoRequest rq(cx); double tmp; WARN_IF_NOT(v.isNumber(), v); - if (!JS::ToNumber(cx, v, &tmp)) + if (!JS::ToNumber(rq.cx, v, &tmp)) return false; out = tmp; return true; } -template<> bool ScriptInterface::FromJSVal(JSContext* cx, JS::HandleValue v, double& out) +template<> bool ScriptInterface::FromJSVal(const Request& rq, JS::HandleValue v, double& out) { - JSAutoRequest rq(cx); WARN_IF_NOT(v.isNumber(), v); - if (!JS::ToNumber(cx, v, &out)) + if (!JS::ToNumber(rq.cx, v, &out)) return false; return true; } -template<> bool ScriptInterface::FromJSVal(JSContext* cx, JS::HandleValue v, i32& out) +template<> bool ScriptInterface::FromJSVal(const Request& rq, JS::HandleValue v, i32& out) { - JSAutoRequest rq(cx); WARN_IF_NOT(v.isNumber(), v); - if (!JS::ToInt32(cx, v, &out)) + if (!JS::ToInt32(rq.cx, v, &out)) return false; return true; } -template<> bool ScriptInterface::FromJSVal(JSContext* cx, JS::HandleValue v, u32& out) +template<> bool ScriptInterface::FromJSVal(const Request& rq, JS::HandleValue v, u32& out) { - JSAutoRequest rq(cx); WARN_IF_NOT(v.isNumber(), v); - if (!JS::ToUint32(cx, v, &out)) + if (!JS::ToUint32(rq.cx, v, &out)) return false; return true; } -template<> bool ScriptInterface::FromJSVal(JSContext* cx, JS::HandleValue v, u16& out) +template<> bool ScriptInterface::FromJSVal(const Request& rq, JS::HandleValue v, u16& out) { - JSAutoRequest rq(cx); WARN_IF_NOT(v.isNumber(), v); - if (!JS::ToUint16(cx, v, &out)) + if (!JS::ToUint16(rq.cx, v, &out)) return false; return true; } -template<> bool ScriptInterface::FromJSVal(JSContext* cx, JS::HandleValue v, u8& out) +template<> bool ScriptInterface::FromJSVal(const Request& rq, JS::HandleValue v, u8& out) { - JSAutoRequest rq(cx); u16 tmp; WARN_IF_NOT(v.isNumber(), v); - if (!JS::ToUint16(cx, v, &tmp)) + if (!JS::ToUint16(rq.cx, v, &tmp)) return false; out = (u8)tmp; return true; } -template<> bool ScriptInterface::FromJSVal(JSContext* cx, JS::HandleValue v, std::wstring& out) +template<> bool ScriptInterface::FromJSVal(const Request& rq, JS::HandleValue v, std::wstring& out) { - JSAutoRequest rq(cx); WARN_IF_NOT(v.isString() || v.isNumber(), v); // allow implicit number conversions - JS::RootedString str(cx, JS::ToString(cx, v)); + JS::RootedString str(rq.cx, JS::ToString(rq.cx, v)); if (!str) FAIL("Argument must be convertible to a string"); @@ -128,7 +120,7 @@ { size_t length; JS::AutoCheckCannotGC nogc; - const JS::Latin1Char* ch = JS_GetLatin1StringCharsAndLength(cx, nogc, str, &length); + const JS::Latin1Char* ch = JS_GetLatin1StringCharsAndLength(rq.cx, nogc, str, &length); if (!ch) FAIL("JS_GetLatin1StringCharsAndLength failed"); @@ -138,7 +130,7 @@ { size_t length; JS::AutoCheckCannotGC nogc; - const char16_t* ch = JS_GetTwoByteStringCharsAndLength(cx, nogc, str, &length); + const char16_t* ch = JS_GetTwoByteStringCharsAndLength(rq.cx, nogc, str, &length); if (!ch) FAIL("JS_GetTwoByteStringsCharsAndLength failed"); // out of memory @@ -147,57 +139,56 @@ return true; } -template<> bool ScriptInterface::FromJSVal(JSContext* cx, JS::HandleValue v, Path& out) +template<> bool ScriptInterface::FromJSVal(const Request& rq, JS::HandleValue v, Path& out) { std::wstring string; - if (!FromJSVal(cx, v, string)) + if (!FromJSVal(rq, v, string)) return false; out = string; return true; } -template<> bool ScriptInterface::FromJSVal(JSContext* cx, JS::HandleValue v, std::string& out) +template<> bool ScriptInterface::FromJSVal(const Request& rq, JS::HandleValue v, std::string& out) { std::wstring wideout; - if (!FromJSVal(cx, v, wideout)) + if (!FromJSVal(rq, v, wideout)) return false; out = CStrW(wideout).ToUTF8(); return true; } -template<> bool ScriptInterface::FromJSVal(JSContext* cx, JS::HandleValue v, CStr8& out) +template<> bool ScriptInterface::FromJSVal(const Request& rq, JS::HandleValue v, CStr8& out) { - return ScriptInterface::FromJSVal(cx, v, static_cast(out)); + return ScriptInterface::FromJSVal(rq, v, static_cast(out)); } -template<> bool ScriptInterface::FromJSVal(JSContext* cx, JS::HandleValue v, CStrW& out) +template<> bool ScriptInterface::FromJSVal(const Request& rq, JS::HandleValue v, CStrW& out) { - return ScriptInterface::FromJSVal(cx, v, static_cast(out)); + return ScriptInterface::FromJSVal(rq, v, static_cast(out)); } -template<> bool ScriptInterface::FromJSVal(JSContext* cx, JS::HandleValue v, Entity& out) +template<> bool ScriptInterface::FromJSVal(const Request& rq, JS::HandleValue v, Entity& out) { - JSAutoRequest rq(cx); if (!v.isObject()) FAIL("Argument must be an object"); - JS::RootedObject obj(cx, &v.toObject()); - JS::RootedValue templateName(cx); - JS::RootedValue id(cx); - JS::RootedValue player(cx); - JS::RootedValue position(cx); - JS::RootedValue rotation(cx); + JS::RootedObject obj(rq.cx, &v.toObject()); + JS::RootedValue templateName(rq.cx); + JS::RootedValue id(rq.cx); + JS::RootedValue player(rq.cx); + JS::RootedValue position(rq.cx); + JS::RootedValue rotation(rq.cx); // TODO: Report type errors - if (!JS_GetProperty(cx, obj, "player", &player) || !FromJSVal(cx, player, out.playerID)) + if (!JS_GetProperty(rq.cx, obj, "player", &player) || !FromJSVal(rq, player, out.playerID)) FAIL("Failed to read Entity.player property"); - if (!JS_GetProperty(cx, obj, "templateName", &templateName) || !FromJSVal(cx, templateName, out.templateName)) + if (!JS_GetProperty(rq.cx, obj, "templateName", &templateName) || !FromJSVal(rq, templateName, out.templateName)) FAIL("Failed to read Entity.templateName property"); - if (!JS_GetProperty(cx, obj, "id", &id) || !FromJSVal(cx, id, out.entityID)) + if (!JS_GetProperty(rq.cx, obj, "id", &id) || !FromJSVal(rq, id, out.entityID)) FAIL("Failed to read Entity.id property"); - if (!JS_GetProperty(cx, obj, "position", &position) || !FromJSVal(cx, position, out.position)) + if (!JS_GetProperty(rq.cx, obj, "position", &position) || !FromJSVal(rq, position, out.position)) FAIL("Failed to read Entity.position property"); - if (!JS_GetProperty(cx, obj, "rotation", &rotation) || !FromJSVal(cx, rotation, out.rotation)) + if (!JS_GetProperty(rq.cx, obj, "rotation", &rotation) || !FromJSVal(rq, rotation, out.rotation)) FAIL("Failed to read Entity.rotation property"); return true; @@ -206,71 +197,69 @@ //////////////////////////////////////////////////////////////// // Primitive types: -template<> void ScriptInterface::ToJSVal(JSContext* UNUSED(cx), JS::MutableHandleValue ret, const bool& val) +template<> void ScriptInterface::ToJSVal(const Request& UNUSED(rq), JS::MutableHandleValue ret, const bool& val) { ret.setBoolean(val); } -template<> void ScriptInterface::ToJSVal(JSContext* UNUSED(cx), JS::MutableHandleValue ret, const float& val) +template<> void ScriptInterface::ToJSVal(const Request& UNUSED(rq), JS::MutableHandleValue ret, const float& val) { ret.set(JS::NumberValue(val)); } -template<> void ScriptInterface::ToJSVal(JSContext* UNUSED(cx), JS::MutableHandleValue ret, const double& val) +template<> void ScriptInterface::ToJSVal(const Request& UNUSED(rq), JS::MutableHandleValue ret, const double& val) { ret.set(JS::NumberValue(val)); } -template<> void ScriptInterface::ToJSVal(JSContext* UNUSED(cx), JS::MutableHandleValue ret, const i32& val) +template<> void ScriptInterface::ToJSVal(const Request& UNUSED(rq), JS::MutableHandleValue ret, const i32& val) { ret.set(JS::NumberValue(val)); } -template<> void ScriptInterface::ToJSVal(JSContext* UNUSED(cx), JS::MutableHandleValue ret, const u16& val) +template<> void ScriptInterface::ToJSVal(const Request& UNUSED(rq), JS::MutableHandleValue ret, const u16& val) { ret.set(JS::NumberValue(val)); } -template<> void ScriptInterface::ToJSVal(JSContext* UNUSED(cx), JS::MutableHandleValue ret, const u8& val) +template<> void ScriptInterface::ToJSVal(const Request& UNUSED(rq), JS::MutableHandleValue ret, const u8& val) { ret.set(JS::NumberValue(val)); } -template<> void ScriptInterface::ToJSVal(JSContext* UNUSED(cx), JS::MutableHandleValue ret, const u32& val) +template<> void ScriptInterface::ToJSVal(const Request& UNUSED(rq), JS::MutableHandleValue ret, const u32& val) { ret.set(JS::NumberValue(val)); } -template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const std::wstring& val) +template<> void ScriptInterface::ToJSVal(const Request& rq, JS::MutableHandleValue ret, const std::wstring& val) { - JSAutoRequest rq(cx); utf16string utf16(val.begin(), val.end()); - JS::RootedString str(cx, JS_NewUCStringCopyN(cx, reinterpret_cast (utf16.c_str()), utf16.length())); + JS::RootedString str(rq.cx, JS_NewUCStringCopyN(rq.cx, reinterpret_cast (utf16.c_str()), utf16.length())); if (str) ret.setString(str); else ret.setUndefined(); } -template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const Path& val) +template<> void ScriptInterface::ToJSVal(const Request& rq, JS::MutableHandleValue ret, const Path& val) { - ToJSVal(cx, ret, val.string()); + ToJSVal(rq, ret, val.string()); } -template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const std::string& val) +template<> void ScriptInterface::ToJSVal(const Request& rq, JS::MutableHandleValue ret, const std::string& val) { - ToJSVal(cx, ret, static_cast(CStr(val).FromUTF8())); + ToJSVal(rq, ret, static_cast(CStr(val).FromUTF8())); } -template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const wchar_t* const& val) +template<> void ScriptInterface::ToJSVal(const Request& rq, JS::MutableHandleValue ret, const wchar_t* const& val) { - ToJSVal(cx, ret, std::wstring(val)); + ToJSVal(rq, ret, std::wstring(val)); } -template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const char* const& val) +template<> void ScriptInterface::ToJSVal(const Request& rq, JS::MutableHandleValue ret, const char* const& val) { - JSAutoRequest rq(cx); - JS::RootedString str(cx, JS_NewStringCopyZ(cx, val)); + JS::RootedString str(rq.cx, JS_NewStringCopyZ(rq.cx, val)); if (str) ret.setString(str); else @@ -278,13 +267,13 @@ } #define TOJSVAL_CHAR(N) \ -template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const wchar_t (&val)[N]) \ +template<> void ScriptInterface::ToJSVal(const Request& rq, JS::MutableHandleValue ret, const wchar_t (&val)[N]) \ { \ - ToJSVal(cx, ret, static_cast(val)); \ + ToJSVal(rq, ret, static_cast(val)); \ } \ -template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const char (&val)[N]) \ +template<> void ScriptInterface::ToJSVal(const Request& rq, JS::MutableHandleValue ret, const char (&val)[N]) \ { \ - ToJSVal(cx, ret, static_cast(val)); \ + ToJSVal(rq, ret, static_cast(val)); \ } TOJSVAL_CHAR(3) @@ -311,14 +300,14 @@ TOJSVAL_CHAR(256) #undef TOJSVAL_CHAR -template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const CStrW& val) +template<> void ScriptInterface::ToJSVal(const Request& rq, JS::MutableHandleValue ret, const CStrW& val) { - ToJSVal(cx, ret, static_cast(val)); + ToJSVal(rq, ret, static_cast(val)); } -template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const CStr8& val) +template<> void ScriptInterface::ToJSVal(const Request& rq, JS::MutableHandleValue ret, const CStr8& val) { - ToJSVal(cx, ret, static_cast(val)); + ToJSVal(rq, ret, static_cast(val)); } //////////////////////////////////////////////////////////////// @@ -337,27 +326,27 @@ class IComponent; -template<> void ScriptInterface::ToJSVal >(JSContext* cx, JS::MutableHandleValue ret, const std::vector& val) +template<> void ScriptInterface::ToJSVal >(const Request& rq, JS::MutableHandleValue ret, const std::vector& val) { - ToJSVal_vector(cx, ret, val); + ToJSVal_vector(rq, ret, val); } -template<> bool ScriptInterface::FromJSVal >(JSContext* cx, JS::HandleValue v, std::vector& out) +template<> bool ScriptInterface::FromJSVal >(const Request& rq, JS::HandleValue v, std::vector& out) { - return FromJSVal_vector(cx, v, out); + return FromJSVal_vector(rq, v, out); } -template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const CVector2D& val) +template<> void ScriptInterface::ToJSVal(const Request& rq, JS::MutableHandleValue ret, const CVector2D& val) { std::vector vec = {val.X, val.Y}; - ToJSVal_vector(cx, ret, vec); + ToJSVal_vector(rq, ret, vec); } -template<> bool ScriptInterface::FromJSVal(JSContext* cx, JS::HandleValue v, CVector2D& out) +template<> bool ScriptInterface::FromJSVal(const Request& rq, JS::HandleValue v, CVector2D& out) { std::vector vec; - if (!FromJSVal_vector(cx, v, vec)) + if (!FromJSVal_vector(rq, v, vec)) return false; if (vec.size() != 2) Index: ps/trunk/source/scriptinterface/ScriptInterface.h =================================================================== --- ps/trunk/source/scriptinterface/ScriptInterface.h +++ ps/trunk/source/scriptinterface/ScriptInterface.h @@ -91,11 +91,30 @@ void SetCallbackData(void* pCBData); static CxPrivate* GetScriptInterfaceAndCBData(JSContext* cx); - JSContext* GetContext() const; JSRuntime* GetJSRuntime() const; shared_ptr GetRuntime() const; /** + * RAII structure which encapsulates an access to the context of a ScriptInterface. + * This struct provides a pointer to the context, and it acts like JSAutoRequest. This + * way, getting the context is safe with respect to the GC. + */ + struct Request + { + Request() = delete; + Request(const Request& rq) = delete; + Request& operator=(const Request& rq) = delete; + Request(const ScriptInterface& scriptInterface); + Request(const ScriptInterface* scriptInterface) : Request(*scriptInterface) {} + Request(shared_ptr scriptInterface) : Request(*scriptInterface) {} + Request(const CxPrivate* cxPrivate) : Request(cxPrivate->pScriptInterface) {} + ~Request(); + + JSContext* cx; + }; + friend struct Request; + + /** * Load global scripts that most script contexts need, * located in the /globalscripts directory. VFS must be initialized. */ @@ -123,12 +142,11 @@ * Can throw an exception. */ template - static bool CreateObject(JSContext* cx, JS::MutableHandleValue objectValue, Args const&... args) + static bool CreateObject(const Request& rq, JS::MutableHandleValue objectValue, Args const&... args) { - JSAutoRequest rq(cx); - JS::RootedObject obj(cx); + JS::RootedObject obj(rq.cx); - if (!CreateObject_(cx, &obj, args...)) + if (!CreateObject_(rq, &obj, args...)) return false; objectValue.setObject(*obj); @@ -138,7 +156,7 @@ /** * Sets the given value to a new JS object or Null Value in case of out-of-memory. */ - static void CreateArray(JSContext* cx, JS::MutableHandleValue objectValue, size_t length = 0); + static void CreateArray(const Request& rq, JS::MutableHandleValue objectValue, size_t length = 0); JS::Value GetGlobalObject() const; @@ -283,7 +301,7 @@ /** * Convert a JS::Value to a C++ type. (This might trigger GC.) */ - template static bool FromJSVal(JSContext* cx, const JS::HandleValue val, T& ret); + template static bool FromJSVal(const Request& rq, const JS::HandleValue val, T& ret); /** * Convert a C++ type to a JS::Value. (This might trigger GC. The return @@ -292,12 +310,12 @@ * The reason is a memory corruption problem that appears to be caused by a bug in Visual Studio. * Details here: http://www.wildfiregames.com/forum/index.php?showtopic=17289&p=285921 */ - template static void ToJSVal(JSContext* cx, JS::MutableHandleValue ret, T const& val); + template static void ToJSVal(const Request& rq, JS::MutableHandleValue ret, T const& val); /** * Convert a named property of an object to a C++ type. */ - template static bool FromJSProperty(JSContext* cx, const JS::HandleValue val, const char* name, T& ret, bool strict = false); + template static bool FromJSProperty(const Request& rq, const JS::HandleValue val, const char* name, T& ret, bool strict = false); /** * MathRandom (this function) calls the random number generator assigned to this ScriptInterface instance and @@ -331,12 +349,11 @@ * Retrieve the private data field of a JSObject that is an instance of the given JSClass. */ template - static T* GetPrivate(JSContext* cx, JS::HandleObject thisobj, JSClass* jsClass) + static T* GetPrivate(const Request& rq, JS::HandleObject thisobj, JSClass* jsClass) { - JSAutoRequest rq(cx); - T* value = static_cast(JS_GetInstancePrivate(cx, thisobj, jsClass, nullptr)); - if (value == nullptr && !JS_IsExceptionPending(cx)) - JS_ReportError(cx, "Private data of the given object is null!"); + T* value = static_cast(JS_GetInstancePrivate(rq.cx, thisobj, jsClass, nullptr)); + if (value == nullptr && !JS_IsExceptionPending(rq.cx)) + JS_ReportError(rq.cx, "Private data of the given object is null!"); return value; } @@ -345,18 +362,17 @@ * If an error occurs, GetPrivate will report it with the according stack. */ template - static T* GetPrivate(JSContext* cx, JS::CallArgs& callArgs, JSClass* jsClass) + static T* GetPrivate(const Request& rq, JS::CallArgs& callArgs, JSClass* jsClass) { - JSAutoRequest rq(cx); if (!callArgs.thisv().isObject()) { - JS_ReportError(cx, "Cannot retrieve private JS class data because from a non-object value!"); + JS_ReportError(rq.cx, "Cannot retrieve private JS class data because from a non-object value!"); return nullptr; } - JS::RootedObject thisObj(cx, &callArgs.thisv().toObject()); - T* value = static_cast(JS_GetInstancePrivate(cx, thisObj, jsClass, &callArgs)); - if (value == nullptr && !JS_IsExceptionPending(cx)) - JS_ReportError(cx, "Private data of the given object is null!"); + JS::RootedObject thisObj(rq.cx, &callArgs.thisv().toObject()); + T* value = static_cast(JS_GetInstancePrivate(rq.cx, thisObj, jsClass, &callArgs)); + if (value == nullptr && !JS_IsExceptionPending(rq.cx)) + JS_ReportError(rq.cx, "Private data of the given object is null!"); return value; } @@ -369,7 +385,7 @@ * because "conversions" from JS::HandleValue to JS::MutableHandleValue are unusual and should not happen "by accident". */ template - static void AssignOrToJSVal(JSContext* cx, JS::MutableHandleValue handle, const T& a); + static void AssignOrToJSVal(const Request& rq, JS::MutableHandleValue handle, const T& a); /** * The same as AssignOrToJSVal, but also allows JS::Value for T. @@ -379,9 +395,9 @@ * "unrooted" version of AssignOrToJSVal. */ template - static void AssignOrToJSValUnrooted(JSContext* cx, JS::MutableHandleValue handle, const T& a) + static void AssignOrToJSValUnrooted(const Request& rq, JS::MutableHandleValue handle, const T& a) { - AssignOrToJSVal(cx, handle, a); + AssignOrToJSVal(rq, handle, a); } /** @@ -390,23 +406,19 @@ * other types. */ template - static T AssignOrFromJSVal(JSContext* cx, const JS::HandleValue& val, bool& ret); + static T AssignOrFromJSVal(const Request& rq, const JS::HandleValue& val, bool& ret); private: - /** - * Careful, the CreateObject_ helpers avoid creation of the JSAutoRequest! - */ - static bool CreateObject_(JSContext* cx, JS::MutableHandleObject obj); + static bool CreateObject_(const Request& rq, JS::MutableHandleObject obj); template - static bool CreateObject_(JSContext* cx, JS::MutableHandleObject obj, const char* propertyName, const T& propertyValue, Args const&... args) + static bool CreateObject_(const Request& rq, JS::MutableHandleObject obj, const char* propertyName, const T& propertyValue, Args const&... args) { - // JSAutoRequest is the responsibility of the caller - JS::RootedValue val(cx); - AssignOrToJSVal(cx, &val, propertyValue); + JS::RootedValue val(rq.cx); + AssignOrToJSVal(rq, &val, propertyValue); - return CreateObject_(cx, obj, args...) && JS_DefineProperty(cx, obj, propertyName, val, JSPROP_ENUMERATE); + return CreateObject_(rq, obj, args...) && JS_DefineProperty(rq.cx, obj, propertyName, val, JSPROP_ENUMERATE); } bool CallFunction_(JS::HandleValue val, const char* name, JS::HandleValueArray argv, JS::MutableHandleValue ret) const; @@ -418,7 +430,7 @@ bool SetPropertyInt_(JS::HandleValue obj, int name, JS::HandleValue value, bool constant, bool enumerate) const; bool GetProperty_(JS::HandleValue obj, const char* name, JS::MutableHandleValue out) const; bool GetPropertyInt_(JS::HandleValue obj, int name, JS::MutableHandleValue value) const; - static bool IsExceptionPending(JSContext* cx); + static bool IsExceptionPending(const Request& rq); struct CustomType { @@ -472,51 +484,51 @@ #include "NativeWrapperDefns.h" template -inline void ScriptInterface::AssignOrToJSVal(JSContext* cx, JS::MutableHandleValue handle, const T& a) +inline void ScriptInterface::AssignOrToJSVal(const Request& rq, JS::MutableHandleValue handle, const T& a) { - ToJSVal(cx, handle, a); + ToJSVal(rq, handle, a); } template<> -inline void ScriptInterface::AssignOrToJSVal(JSContext* UNUSED(cx), JS::MutableHandleValue handle, const JS::PersistentRootedValue& a) +inline void ScriptInterface::AssignOrToJSVal(const Request& UNUSED(rq), JS::MutableHandleValue handle, const JS::PersistentRootedValue& a) { handle.set(a); } template<> -inline void ScriptInterface::AssignOrToJSVal >(JSContext* UNUSED(cx), JS::MutableHandleValue handle, const JS::Heap& a) +inline void ScriptInterface::AssignOrToJSVal >(const Request& UNUSED(rq), JS::MutableHandleValue handle, const JS::Heap& a) { handle.set(a); } template<> -inline void ScriptInterface::AssignOrToJSVal(JSContext* UNUSED(cx), JS::MutableHandleValue handle, const JS::RootedValue& a) +inline void ScriptInterface::AssignOrToJSVal(const Request& UNUSED(rq), JS::MutableHandleValue handle, const JS::RootedValue& a) { handle.set(a); } template <> -inline void ScriptInterface::AssignOrToJSVal(JSContext* UNUSED(cx), JS::MutableHandleValue handle, const JS::HandleValue& a) +inline void ScriptInterface::AssignOrToJSVal(const Request& UNUSED(rq), JS::MutableHandleValue handle, const JS::HandleValue& a) { handle.set(a); } template <> -inline void ScriptInterface::AssignOrToJSValUnrooted(JSContext* UNUSED(cx), JS::MutableHandleValue handle, const JS::Value& a) +inline void ScriptInterface::AssignOrToJSValUnrooted(const Request& UNUSED(rq), JS::MutableHandleValue handle, const JS::Value& a) { handle.set(a); } template -inline T ScriptInterface::AssignOrFromJSVal(JSContext* cx, const JS::HandleValue& val, bool& ret) +inline T ScriptInterface::AssignOrFromJSVal(const Request& rq, const JS::HandleValue& val, bool& ret) { T retVal; - ret = FromJSVal(cx, val, retVal); + ret = FromJSVal(rq, val, retVal); return retVal; } template<> -inline JS::HandleValue ScriptInterface::AssignOrFromJSVal(JSContext* UNUSED(cx), const JS::HandleValue& val, bool& ret) +inline JS::HandleValue ScriptInterface::AssignOrFromJSVal(const Request& UNUSED(rq), const JS::HandleValue& val, bool& ret) { ret = true; return val; @@ -525,58 +537,57 @@ template bool ScriptInterface::SetGlobal(const char* name, const T& value, bool replace, bool constant, bool enumerate) { - JSAutoRequest rq(GetContext()); - JS::RootedValue val(GetContext()); - AssignOrToJSVal(GetContext(), &val, value); + Request rq(this); + JS::RootedValue val(rq.cx); + AssignOrToJSVal(rq, &val, value); return SetGlobal_(name, val, replace, constant, enumerate); } template bool ScriptInterface::SetProperty(JS::HandleValue obj, const char* name, const T& value, bool constant, bool enumerate) const { - JSAutoRequest rq(GetContext()); - JS::RootedValue val(GetContext()); - AssignOrToJSVal(GetContext(), &val, value); + Request rq(this); + JS::RootedValue val(rq.cx); + AssignOrToJSVal(rq, &val, value); return SetProperty_(obj, name, val, constant, enumerate); } template bool ScriptInterface::SetProperty(JS::HandleValue obj, const wchar_t* name, const T& value, bool constant, bool enumerate) const { - JSAutoRequest rq(GetContext()); - JS::RootedValue val(GetContext()); - AssignOrToJSVal(GetContext(), &val, value); + Request rq(this); + JS::RootedValue val(rq.cx); + AssignOrToJSVal(rq, &val, value); return SetProperty_(obj, name, val, constant, enumerate); } template bool ScriptInterface::SetPropertyInt(JS::HandleValue obj, int name, const T& value, bool constant, bool enumerate) const { - JSAutoRequest rq(GetContext()); - JS::RootedValue val(GetContext()); - AssignOrToJSVal(GetContext(), &val, value); + Request rq(this); + JS::RootedValue val(rq.cx); + AssignOrToJSVal(rq, &val, value); return SetPropertyInt_(obj, name, val, constant, enumerate); } template bool ScriptInterface::GetProperty(JS::HandleValue obj, const char* name, T& out) const { - JSContext* cx = GetContext(); - JSAutoRequest rq(cx); - JS::RootedValue val(cx); + Request rq(this); + JS::RootedValue val(rq.cx); if (!GetProperty_(obj, name, &val)) return false; - return FromJSVal(cx, val, out); + return FromJSVal(rq, val, out); } template bool ScriptInterface::GetPropertyInt(JS::HandleValue obj, int name, T& out) const { - JSAutoRequest rq(GetContext()); - JS::RootedValue val(GetContext()); + Request rq(this); + JS::RootedValue val(rq.cx); if (!GetPropertyInt_(obj, name, &val)) return false; - return FromJSVal(GetContext(), val, out); + return FromJSVal(rq, val, out); } template @@ -590,11 +601,11 @@ template bool ScriptInterface::Eval(const CHAR* code, T& ret) const { - JSAutoRequest rq(GetContext()); - JS::RootedValue rval(GetContext()); + Request rq(this); + JS::RootedValue rval(rq.cx); if (!Eval_(code, &rval)) return false; - return FromJSVal(GetContext(), rval, ret); + return FromJSVal(rq, rval, ret); } #endif // INCLUDED_SCRIPTINTERFACE Index: ps/trunk/source/scriptinterface/ScriptInterface.cpp =================================================================== --- ps/trunk/source/scriptinterface/ScriptInterface.cpp +++ ps/trunk/source/scriptinterface/ScriptInterface.cpp @@ -62,13 +62,27 @@ // members have to be called before the runtime destructor. shared_ptr m_runtime; - JSContext* m_cx; JS::PersistentRootedObject m_glob; // global scope object - JSCompartment* m_formerCompartment; boost::rand48* m_rng; JS::PersistentRootedObject m_nativeScope; // native function scope object + + friend ScriptInterface::Request; + private: + JSContext* m_cx; + JSCompartment* m_formerCompartment; }; +ScriptInterface::Request::Request(const ScriptInterface& scriptInterface) : + cx(scriptInterface.m->m_cx) +{ + JS_BeginRequest(cx); +} + +ScriptInterface::Request::~Request() +{ + JS_EndRequest(cx); +} + namespace { @@ -83,7 +97,7 @@ void ErrorReporter(JSContext* cx, const char* message, JSErrorReport* report) { - JSAutoRequest rq(cx); + ScriptInterface::Request rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); std::stringstream msg; bool isWarning = JSREPORT_IS_WARNING(report->flags); @@ -105,7 +119,7 @@ JS_GetProperty(cx, excnObj, "stack", &stackVal); std::string stackText; - ScriptInterface::FromJSVal(cx, stackVal, stackText); + ScriptInterface::FromJSVal(rq, stackVal, stackText); std::istringstream stream(stackText); for (std::string line; std::getline(stream, line);) @@ -126,10 +140,12 @@ bool print(JSContext* cx, uint argc, JS::Value* vp) { JS::CallArgs args = JS::CallArgsFromVp(argc, vp); + ScriptInterface::Request rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); \ + for (uint i = 0; i < args.length(); ++i) { std::wstring str; - if (!ScriptInterface::FromJSVal(cx, args[i], str)) + if (!ScriptInterface::FromJSVal(rq, args[i], str)) return false; debug_printf("%s", utf8_from_wstring(str).c_str()); } @@ -147,8 +163,9 @@ return true; } + ScriptInterface::Request rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); \ std::wstring str; - if (!ScriptInterface::FromJSVal(cx, args[0], str)) + if (!ScriptInterface::FromJSVal(rq, args[0], str)) return false; LOGMESSAGE("%s", utf8_from_wstring(str)); args.rval().setUndefined(); @@ -164,8 +181,9 @@ return true; } + ScriptInterface::Request rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); \ std::wstring str; - if (!ScriptInterface::FromJSVal(cx, args[0], str)) + if (!ScriptInterface::FromJSVal(rq, args[0], str)) return false; LOGWARNING("%s", utf8_from_wstring(str)); args.rval().setUndefined(); @@ -181,8 +199,9 @@ return true; } + ScriptInterface::Request rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); \ std::wstring str; - if (!ScriptInterface::FromJSVal(cx, args[0], str)) + if (!ScriptInterface::FromJSVal(rq, args[0], str)) return false; LOGERROR("%s", utf8_from_wstring(str)); args.rval().setUndefined(); @@ -191,8 +210,6 @@ bool deepcopy(JSContext* cx, uint argc, JS::Value* vp) { - JSAutoRequest rq(cx); - JS::CallArgs args = JS::CallArgsFromVp(argc, vp); if (args.length() < 1) { @@ -200,8 +217,9 @@ return true; } + ScriptInterface::Request rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); \ JS::RootedValue ret(cx); - if (!JS_StructuredClone(cx, args[0], &ret, NULL, NULL)) + if (!JS_StructuredClone(rq.cx, args[0], &ret, NULL, NULL)) return false; args.rval().set(ret); @@ -211,10 +229,10 @@ bool deepfreeze(JSContext* cx, uint argc, JS::Value* vp) { JS::CallArgs args = JS::CallArgsFromVp(argc, vp); + ScriptInterface::Request rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); \ if (args.length() != 1 || !args.get(0).isObject()) { - JSAutoRequest rq(cx); JS_ReportError(cx, "deepfreeze requires exactly one object as an argument."); return false; } @@ -229,10 +247,11 @@ const char* name = "(ProfileStart)"; JS::CallArgs args = JS::CallArgsFromVp(argc, vp); + ScriptInterface::Request rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); \ if (args.length() >= 1) { std::string str; - if (!ScriptInterface::FromJSVal(cx, args[0], str)) + if (!ScriptInterface::FromJSVal(rq, args[0], str)) return false; typedef boost::flyweight< @@ -270,10 +289,11 @@ const char* name = "(ProfileAttribute)"; JS::CallArgs args = JS::CallArgsFromVp(argc, vp); + ScriptInterface::Request rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); \ if (args.length() >= 1) { std::string str; - if (!ScriptInterface::FromJSVal(cx, args[0], str)) + if (!ScriptInterface::FromJSVal(rq, args[0], str)) return false; typedef boost::flyweight< @@ -409,8 +429,9 @@ g_ScriptStatsTable->Add(this, debugName); } + Request rq(this); m_CxPrivate.pScriptInterface = this; - JS_SetContextPrivate(m->m_cx, (void*)&m_CxPrivate); + JS_SetContextPrivate(rq.cx, (void*)&m_CxPrivate); } ScriptInterface::~ScriptInterface() @@ -455,13 +476,13 @@ bool ScriptInterface::ReplaceNondeterministicRNG(boost::rand48& rng) { - JSAutoRequest rq(m->m_cx); - JS::RootedValue math(m->m_cx); - JS::RootedObject global(m->m_cx, m->m_glob); - if (JS_GetProperty(m->m_cx, global, "Math", &math) && math.isObject()) + Request rq(this); + JS::RootedValue math(rq.cx); + JS::RootedObject global(rq.cx, m->m_glob); + if (JS_GetProperty(rq.cx, global, "Math", &math) && math.isObject()) { - JS::RootedObject mathObj(m->m_cx, &math.toObject()); - JS::RootedFunction random(m->m_cx, JS_DefineFunction(m->m_cx, mathObj, "random", Math_random, 0, + JS::RootedObject mathObj(rq.cx, &math.toObject()); + JS::RootedFunction random(rq.cx, JS_DefineFunction(rq.cx, mathObj, "random", Math_random, 0, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)); if (random) { @@ -479,11 +500,6 @@ m->Register(name, fptr, (uint)nargs); } -JSContext* ScriptInterface::GetContext() const -{ - return m->m_cx; -} - JSRuntime* ScriptInterface::GetJSRuntime() const { return m->m_runtime->m_rt; @@ -496,7 +512,7 @@ void ScriptInterface::CallConstructor(JS::HandleValue ctor, JS::HandleValueArray argv, JS::MutableHandleValue out) const { - JSAutoRequest rq(m->m_cx); + Request rq(this); if (!ctor.isObject()) { LOGERROR("CallConstructor: ctor is not an object"); @@ -504,13 +520,13 @@ return; } - JS::RootedObject ctorObj(m->m_cx, &ctor.toObject()); - out.setObjectOrNull(JS_New(m->m_cx, ctorObj, argv)); + JS::RootedObject ctorObj(rq.cx, &ctor.toObject()); + out.setObjectOrNull(JS_New(rq.cx, ctorObj, argv)); } void ScriptInterface::DefineCustomObjectType(JSClass *clasp, JSNative constructor, uint minArgs, JSPropertySpec *ps, JSFunctionSpec *fs, JSPropertySpec *static_ps, JSFunctionSpec *static_fs) { - JSAutoRequest rq(m->m_cx); + Request rq(this); std::string typeName = clasp->name; if (m_CustomObjectTypes.find(typeName) != m_CustomObjectTypes.end()) @@ -519,8 +535,8 @@ throw PSERROR_Scripting_DefineType_AlreadyExists(); } - JS::RootedObject global(m->m_cx, m->m_glob); - JS::RootedObject obj(m->m_cx, JS_InitClass(m->m_cx, global, nullptr, + JS::RootedObject global(rq.cx, m->m_glob); + JS::RootedObject obj(rq.cx, JS_InitClass(rq.cx, global, nullptr, clasp, constructor, minArgs, // Constructor, min args ps, fs, // Properties, methods @@ -531,7 +547,7 @@ CustomType& type = m_CustomObjectTypes[typeName]; - type.m_Prototype.init(m->m_cx, obj); + type.m_Prototype.init(rq.cx, obj); type.m_Class = clasp; type.m_Constructor = constructor; } @@ -543,31 +559,30 @@ if (it == m_CustomObjectTypes.end()) throw PSERROR_Scripting_TypeDoesNotExist(); - JS::RootedObject prototype(m->m_cx, it->second.m_Prototype.get()); - return JS_NewObjectWithGivenProto(m->m_cx, it->second.m_Class, prototype); + Request rq(this); + JS::RootedObject prototype(rq.cx, it->second.m_Prototype.get()); + return JS_NewObjectWithGivenProto(rq.cx, it->second.m_Class, prototype); } bool ScriptInterface::CallFunction_(JS::HandleValue val, const char* name, JS::HandleValueArray argv, JS::MutableHandleValue ret) const { - JSAutoRequest rq(m->m_cx); - JS::RootedObject obj(m->m_cx); - if (!JS_ValueToObject(m->m_cx, val, &obj) || !obj) + Request rq(this); + JS::RootedObject obj(rq.cx); + if (!JS_ValueToObject(rq.cx, val, &obj) || !obj) return false; // Check that the named function actually exists, to avoid ugly JS error reports // when calling an undefined value bool found; - if (!JS_HasProperty(m->m_cx, obj, name, &found) || !found) + if (!JS_HasProperty(rq.cx, obj, name, &found) || !found) return false; - return JS_CallFunctionName(m->m_cx, obj, name, argv, ret); + return JS_CallFunctionName(rq.cx, obj, name, argv, ret); } -bool ScriptInterface::CreateObject_(JSContext* cx, JS::MutableHandleObject object) +bool ScriptInterface::CreateObject_(const Request& rq, JS::MutableHandleObject object) { - // JSAutoRequest is the responsibility of the caller - - object.set(JS_NewPlainObject(cx)); + object.set(JS_NewPlainObject(rq.cx)); if (!object) throw PSERROR_Scripting_CreateObjectFailed(); @@ -575,40 +590,38 @@ return true; } -void ScriptInterface::CreateArray(JSContext* cx, JS::MutableHandleValue objectValue, size_t length) +void ScriptInterface::CreateArray(const Request& rq, JS::MutableHandleValue objectValue, size_t length) { - JSAutoRequest rq(cx); - - objectValue.setObjectOrNull(JS_NewArrayObject(cx, length)); + objectValue.setObjectOrNull(JS_NewArrayObject(rq.cx, length)); if (!objectValue.isObject()) throw PSERROR_Scripting_CreateObjectFailed(); } JS::Value ScriptInterface::GetGlobalObject() const { - JSAutoRequest rq(m->m_cx); - return JS::ObjectValue(*JS::CurrentGlobalOrNull(m->m_cx)); + Request rq(this); + return JS::ObjectValue(*JS::CurrentGlobalOrNull(rq.cx)); } bool ScriptInterface::SetGlobal_(const char* name, JS::HandleValue value, bool replace, bool constant, bool enumerate) { - JSAutoRequest rq(m->m_cx); - JS::RootedObject global(m->m_cx, m->m_glob); + Request rq(this); + JS::RootedObject global(rq.cx, m->m_glob); bool found; - if (!JS_HasProperty(m->m_cx, global, name, &found)) + if (!JS_HasProperty(rq.cx, global, name, &found)) return false; if (found) { - JS::Rooted desc(m->m_cx); - if (!JS_GetOwnPropertyDescriptor(m->m_cx, global, name, &desc)) + JS::Rooted desc(rq.cx); + if (!JS_GetOwnPropertyDescriptor(rq.cx, global, name, &desc)) return false; if (!desc.writable()) { if (!replace) { - JS_ReportError(m->m_cx, "SetGlobal \"%s\" called multiple times", name); + JS_ReportError(rq.cx, "SetGlobal \"%s\" called multiple times", name); return false; } @@ -616,12 +629,12 @@ // instead of using SetGlobal. if (!desc.configurable()) { - JS_ReportError(m->m_cx, "The global \"%s\" is permanent and cannot be hotloaded", name); + JS_ReportError(rq.cx, "The global \"%s\" is permanent and cannot be hotloaded", name); return false; } LOGMESSAGE("Hotloading new value for global \"%s\".", name); - ENSURE(JS_DeleteProperty(m->m_cx, global, name)); + ENSURE(JS_DeleteProperty(rq.cx, global, name)); } } @@ -631,12 +644,12 @@ if (enumerate) attrs |= JSPROP_ENUMERATE; - return JS_DefineProperty(m->m_cx, global, name, value, attrs); + return JS_DefineProperty(rq.cx, global, name, value, attrs); } bool ScriptInterface::SetProperty_(JS::HandleValue obj, const char* name, JS::HandleValue value, bool constant, bool enumerate) const { - JSAutoRequest rq(m->m_cx); + Request rq(this); uint attrs = 0; if (constant) attrs |= JSPROP_READONLY | JSPROP_PERMANENT; @@ -645,16 +658,16 @@ if (!obj.isObject()) return false; - JS::RootedObject object(m->m_cx, &obj.toObject()); + JS::RootedObject object(rq.cx, &obj.toObject()); - if (!JS_DefineProperty(m->m_cx, object, name, value, attrs)) + if (!JS_DefineProperty(rq.cx, object, name, value, attrs)) return false; return true; } bool ScriptInterface::SetProperty_(JS::HandleValue obj, const wchar_t* name, JS::HandleValue value, bool constant, bool enumerate) const { - JSAutoRequest rq(m->m_cx); + Request rq(this); uint attrs = 0; if (constant) attrs |= JSPROP_READONLY | JSPROP_PERMANENT; @@ -663,17 +676,17 @@ if (!obj.isObject()) return false; - JS::RootedObject object(m->m_cx, &obj.toObject()); + JS::RootedObject object(rq.cx, &obj.toObject()); utf16string name16(name, name + wcslen(name)); - if (!JS_DefineUCProperty(m->m_cx, object, reinterpret_cast(name16.c_str()), name16.length(), value, attrs)) + if (!JS_DefineUCProperty(rq.cx, object, reinterpret_cast(name16.c_str()), name16.length(), value, attrs)) return false; return true; } bool ScriptInterface::SetPropertyInt_(JS::HandleValue obj, int name, JS::HandleValue value, bool constant, bool enumerate) const { - JSAutoRequest rq(m->m_cx); + Request rq(this); uint attrs = 0; if (constant) attrs |= JSPROP_READONLY | JSPROP_PERMANENT; @@ -682,10 +695,10 @@ if (!obj.isObject()) return false; - JS::RootedObject object(m->m_cx, &obj.toObject()); + JS::RootedObject object(rq.cx, &obj.toObject()); - JS::RootedId id(m->m_cx, INT_TO_JSID(name)); - if (!JS_DefinePropertyById(m->m_cx, object, id, value, attrs)) + JS::RootedId id(rq.cx, INT_TO_JSID(name)); + if (!JS_DefinePropertyById(rq.cx, object, id, value, attrs)) return false; return true; } @@ -697,9 +710,8 @@ bool ScriptInterface::GetProperty(JS::HandleValue obj, const char* name, JS::MutableHandleObject out) const { - JSContext* cx = GetContext(); - JSAutoRequest rq(cx); - JS::RootedValue val(cx); + Request rq(this); + JS::RootedValue val(rq.cx); if (!GetProperty_(obj, name, &val)) return false; if (!val.isObject()) @@ -719,25 +731,25 @@ bool ScriptInterface::GetProperty_(JS::HandleValue obj, const char* name, JS::MutableHandleValue out) const { - JSAutoRequest rq(m->m_cx); + Request rq(this); if (!obj.isObject()) return false; - JS::RootedObject object(m->m_cx, &obj.toObject()); + JS::RootedObject object(rq.cx, &obj.toObject()); - if (!JS_GetProperty(m->m_cx, object, name, out)) + if (!JS_GetProperty(rq.cx, object, name, out)) return false; return true; } bool ScriptInterface::GetPropertyInt_(JS::HandleValue obj, int name, JS::MutableHandleValue out) const { - JSAutoRequest rq(m->m_cx); - JS::RootedId nameId(m->m_cx, INT_TO_JSID(name)); + Request rq(this); + JS::RootedId nameId(rq.cx, INT_TO_JSID(name)); if (!obj.isObject()) return false; - JS::RootedObject object(m->m_cx, &obj.toObject()); + JS::RootedObject object(rq.cx, &obj.toObject()); - if (!JS_GetPropertyById(m->m_cx, object, nameId, out)) + if (!JS_GetPropertyById(rq.cx, object, nameId, out)) return false; return true; } @@ -745,20 +757,20 @@ bool ScriptInterface::HasProperty(JS::HandleValue obj, const char* name) const { // TODO: proper errorhandling - JSAutoRequest rq(m->m_cx); + Request rq(this); if (!obj.isObject()) return false; - JS::RootedObject object(m->m_cx, &obj.toObject()); + JS::RootedObject object(rq.cx, &obj.toObject()); bool found; - if (!JS_HasProperty(m->m_cx, object, name, &found)) + if (!JS_HasProperty(rq.cx, object, name, &found)) return false; return found; } bool ScriptInterface::EnumeratePropertyNames(JS::HandleValue objVal, bool enumerableOnly, std::vector& out) const { - JSAutoRequest rq(m->m_cx); + Request rq(this); if (!objVal.isObjectOrNull()) { @@ -766,18 +778,18 @@ return false; } - JS::RootedObject obj(m->m_cx, &objVal.toObject()); - JS::AutoIdVector props(m->m_cx); + JS::RootedObject obj(rq.cx, &objVal.toObject()); + JS::AutoIdVector props(rq.cx); // This recurses up the prototype chain on its own. - if (!js::GetPropertyKeys(m->m_cx, obj, enumerableOnly? 0 : JSITER_HIDDEN, &props)) + if (!js::GetPropertyKeys(rq.cx, obj, enumerableOnly? 0 : JSITER_HIDDEN, &props)) return false; out.reserve(out.size() + props.length()); for (size_t i = 0; i < props.length(); ++i) { - JS::RootedId id(m->m_cx, props[i]); - JS::RootedValue val(m->m_cx); - if (!JS_IdToValue(m->m_cx, id, &val)) + JS::RootedId id(rq.cx, props[i]); + JS::RootedValue val(rq.cx); + if (!JS_IdToValue(rq.cx, id, &val)) return false; // Ignore integer properties for now. @@ -786,7 +798,7 @@ continue; std::string propName; - if (!FromJSVal(m->m_cx, val, propName)) + if (!FromJSVal(rq, val, propName)) return false; out.emplace_back(std::move(propName)); @@ -797,71 +809,71 @@ bool ScriptInterface::SetPrototype(JS::HandleValue objVal, JS::HandleValue protoVal) { - JSAutoRequest rq(m->m_cx); + Request rq(this); if (!objVal.isObject() || !protoVal.isObject()) return false; - JS::RootedObject obj(m->m_cx, &objVal.toObject()); - JS::RootedObject proto(m->m_cx, &protoVal.toObject()); - return JS_SetPrototype(m->m_cx, obj, proto); + JS::RootedObject obj(rq.cx, &objVal.toObject()); + JS::RootedObject proto(rq.cx, &protoVal.toObject()); + return JS_SetPrototype(rq.cx, obj, proto); } bool ScriptInterface::FreezeObject(JS::HandleValue objVal, bool deep) const { - JSAutoRequest rq(m->m_cx); + Request rq(this); if (!objVal.isObject()) return false; - JS::RootedObject obj(m->m_cx, &objVal.toObject()); + JS::RootedObject obj(rq.cx, &objVal.toObject()); if (deep) - return JS_DeepFreezeObject(m->m_cx, obj); + return JS_DeepFreezeObject(rq.cx, obj); else - return JS_FreezeObject(m->m_cx, obj); + return JS_FreezeObject(rq.cx, obj); } bool ScriptInterface::LoadScript(const VfsPath& filename, const std::string& code) const { - JSAutoRequest rq(m->m_cx); - JS::RootedObject global(m->m_cx, m->m_glob); + Request rq(this); + JS::RootedObject global(rq.cx, m->m_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(m->m_cx); + JS::CompileOptions options(rq.cx); options.setFileAndLine(filenameStr.c_str(), lineNo); options.setIsRunOnce(false); - JS::RootedFunction func(m->m_cx); - JS::AutoObjectVector emptyScopeChain(m->m_cx); - if (!JS::CompileFunction(m->m_cx, emptyScopeChain, options, NULL, 0, NULL, + JS::RootedFunction func(rq.cx); + JS::AutoObjectVector emptyScopeChain(rq.cx); + if (!JS::CompileFunction(rq.cx, emptyScopeChain, options, NULL, 0, NULL, reinterpret_cast(codeUtf16.c_str()), (uint)(codeUtf16.length()), &func)) return false; - JS::RootedValue rval(m->m_cx); - return JS_CallFunction(m->m_cx, nullptr, func, JS::HandleValueArray::empty(), &rval); + JS::RootedValue rval(rq.cx); + return JS_CallFunction(rq.cx, nullptr, func, JS::HandleValueArray::empty(), &rval); } bool ScriptInterface::LoadGlobalScript(const VfsPath& filename, const std::wstring& code) const { - JSAutoRequest rq(m->m_cx); + Request 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(m->m_cx); - JS::CompileOptions opts(m->m_cx); + JS::RootedValue rval(rq.cx); + JS::CompileOptions opts(rq.cx); opts.setFileAndLine(filenameStr.c_str(), lineNo); - return JS::Evaluate(m->m_cx, opts, + return JS::Evaluate(rq.cx, opts, reinterpret_cast(codeUtf16.c_str()), (uint)(codeUtf16.length()), &rval); } bool ScriptInterface::LoadGlobalScriptFile(const VfsPath& path) const { - JSAutoRequest rq(m->m_cx); + Request rq(this); if (!VfsFileExists(path)) { LOGERROR("File '%s' does not exist", path.string8()); @@ -886,68 +898,68 @@ // Passing a temporary string there will cause undefined behaviour, so we create a separate string to avoid the temporary. std::string filenameStr = path.string8(); - JS::RootedValue rval(m->m_cx); - JS::CompileOptions opts(m->m_cx); + JS::RootedValue rval(rq.cx); + JS::CompileOptions opts(rq.cx); opts.setFileAndLine(filenameStr.c_str(), lineNo); - return JS::Evaluate(m->m_cx, opts, + return JS::Evaluate(rq.cx, opts, reinterpret_cast(codeUtf16.c_str()), (uint)(codeUtf16.length()), &rval); } bool ScriptInterface::Eval(const char* code) const { - JSAutoRequest rq(m->m_cx); - JS::RootedValue rval(m->m_cx); + Request rq(this); + JS::RootedValue rval(rq.cx); return Eval_(code, &rval); } bool ScriptInterface::Eval_(const char* code, JS::MutableHandleValue rval) const { - JSAutoRequest rq(m->m_cx); + Request rq(this); utf16string codeUtf16(code, code+strlen(code)); - JS::CompileOptions opts(m->m_cx); + JS::CompileOptions opts(rq.cx); opts.setFileAndLine("(eval)", 1); - return JS::Evaluate(m->m_cx, opts, reinterpret_cast(codeUtf16.c_str()), (uint)codeUtf16.length(), rval); + return JS::Evaluate(rq.cx, opts, reinterpret_cast(codeUtf16.c_str()), (uint)codeUtf16.length(), rval); } bool ScriptInterface::Eval_(const wchar_t* code, JS::MutableHandleValue rval) const { - JSAutoRequest rq(m->m_cx); + Request rq(this); utf16string codeUtf16(code, code+wcslen(code)); - JS::CompileOptions opts(m->m_cx); + JS::CompileOptions opts(rq.cx); opts.setFileAndLine("(eval)", 1); - return JS::Evaluate(m->m_cx, opts, reinterpret_cast(codeUtf16.c_str()), (uint)codeUtf16.length(), rval); + return JS::Evaluate(rq.cx, opts, reinterpret_cast(codeUtf16.c_str()), (uint)codeUtf16.length(), rval); } bool ScriptInterface::ParseJSON(const std::string& string_utf8, JS::MutableHandleValue out) const { - JSAutoRequest rq(m->m_cx); + Request rq(this); std::wstring attrsW = wstring_from_utf8(string_utf8); utf16string string(attrsW.begin(), attrsW.end()); - if (JS_ParseJSON(m->m_cx, reinterpret_cast(string.c_str()), (u32)string.size(), out)) + if (JS_ParseJSON(rq.cx, reinterpret_cast(string.c_str()), (u32)string.size(), out)) return true; LOGERROR("JS_ParseJSON failed!"); - if (!JS_IsExceptionPending(m->m_cx)) + if (!JS_IsExceptionPending(rq.cx)) return false; - JS::RootedValue exc(m->m_cx); - if (!JS_GetPendingException(m->m_cx, &exc)) + JS::RootedValue exc(rq.cx); + if (!JS_GetPendingException(rq.cx, &exc)) return false; - JS_ClearPendingException(m->m_cx); + JS_ClearPendingException(rq.cx); // We expect an object of type SyntaxError if (!exc.isObject()) return false; - JS::RootedValue rval(m->m_cx); - JS::RootedObject excObj(m->m_cx, &exc.toObject()); - if (!JS_CallFunctionName(m->m_cx, excObj, "toString", JS::HandleValueArray::empty(), &rval)) + JS::RootedValue rval(rq.cx); + JS::RootedObject excObj(rq.cx, &exc.toObject()); + if (!JS_CallFunctionName(rq.cx, excObj, "toString", JS::HandleValueArray::empty(), &rval)) return false; std::wstring error; - ScriptInterface::FromJSVal(m->m_cx, rval, error); + ScriptInterface::FromJSVal(rq, rval, error); LOGERROR("%s", utf8_from_wstring(error)); return false; } @@ -995,12 +1007,12 @@ // It probably has historical reasons and could be changed by SpiderMonkey in the future. std::string ScriptInterface::StringifyJSON(JS::MutableHandleValue obj, bool indent) const { - JSAutoRequest rq(m->m_cx); + Request rq(this); Stringifier str; - JS::RootedValue indentVal(m->m_cx, indent ? JS::Int32Value(2) : JS::UndefinedValue()); - if (!JS_Stringify(m->m_cx, obj, nullptr, indentVal, &Stringifier::callback, &str)) + JS::RootedValue indentVal(rq.cx, indent ? JS::Int32Value(2) : JS::UndefinedValue()); + if (!JS_Stringify(rq.cx, obj, nullptr, indentVal, &Stringifier::callback, &str)) { - JS_ClearPendingException(m->m_cx); + JS_ClearPendingException(rq.cx); LOGERROR("StringifyJSON failed"); return std::string(); } @@ -1011,7 +1023,7 @@ std::string ScriptInterface::ToString(JS::MutableHandleValue obj, bool pretty) const { - JSAutoRequest rq(m->m_cx); + Request rq(this); if (obj.isUndefined()) return "(void 0)"; @@ -1021,12 +1033,12 @@ if (pretty) { Stringifier str; - JS::RootedValue indentVal(m->m_cx, JS::Int32Value(2)); + JS::RootedValue indentVal(rq.cx, JS::Int32Value(2)); // Temporary disable the error reporter, so we don't print complaints about cyclic values JSErrorReporter er = JS_SetErrorReporter(m->m_runtime->m_rt, NULL); - bool ok = JS_Stringify(m->m_cx, obj, nullptr, indentVal, &Stringifier::callback, &str); + bool ok = JS_Stringify(rq.cx, obj, nullptr, indentVal, &Stringifier::callback, &str); // Restore error reporter JS_SetErrorReporter(m->m_runtime->m_rt, er); @@ -1035,7 +1047,7 @@ return str.stream.str(); // Clear the exception set when Stringify failed - JS_ClearPendingException(m->m_cx); + JS_ClearPendingException(rq.cx); } // Caller didn't want pretty output, or JSON conversion failed (e.g. due to cycles), @@ -1048,29 +1060,28 @@ void ScriptInterface::ReportError(const char* msg) const { - JSAutoRequest rq(m->m_cx); + Request rq(this); // JS_ReportError by itself doesn't seem to set a JS-style exception, and so // script callers will be unable to catch anything. So use JS_SetPendingException // to make sure there really is a script-level exception. But just set it to undefined // because there's not much value yet in throwing a real exception object. - JS_SetPendingException(m->m_cx, JS::UndefinedHandleValue); + JS_SetPendingException(rq.cx, JS::UndefinedHandleValue); // And report the actual error - JS_ReportError(m->m_cx, "%s", msg); + JS_ReportError(rq.cx, "%s", msg); - // TODO: Why doesn't JS_ReportPendingException(m->m_cx); work? + // TODO: Why doesn't JS_ReportPendingException(cx); work? } -bool ScriptInterface::IsExceptionPending(JSContext* cx) +bool ScriptInterface::IsExceptionPending(const Request& rq) { - JSAutoRequest rq(cx); - return JS_IsExceptionPending(cx) ? true : false; + return JS_IsExceptionPending(rq.cx) ? true : false; } JS::Value ScriptInterface::CloneValueFromOtherContext(const ScriptInterface& otherContext, JS::HandleValue val) const { PROFILE("CloneValueFromOtherContext"); - JSAutoRequest rq(m->m_cx); - JS::RootedValue out(m->m_cx); + Request rq(this); + JS::RootedValue out(rq.cx); shared_ptr structuredClone = otherContext.WriteStructuredClone(val); ReadStructuredClone(structuredClone, &out); return out.get(); @@ -1089,10 +1100,10 @@ shared_ptr ScriptInterface::WriteStructuredClone(JS::HandleValue v) const { - JSAutoRequest rq(m->m_cx); + Request rq(this); u64* data = NULL; size_t nbytes = 0; - if (!JS_WriteStructuredClone(m->m_cx, v, &data, &nbytes, NULL, NULL, JS::UndefinedHandleValue)) + if (!JS_WriteStructuredClone(rq.cx, v, &data, &nbytes, NULL, NULL, JS::UndefinedHandleValue)) { debug_warn(L"Writing a structured clone with JS_WriteStructuredClone failed!"); return shared_ptr(); @@ -1106,6 +1117,6 @@ void ScriptInterface::ReadStructuredClone(const shared_ptr& ptr, JS::MutableHandleValue ret) const { - JSAutoRequest rq(m->m_cx); - JS_ReadStructuredClone(m->m_cx, ptr->m_Data, ptr->m_Size, JS_STRUCTURED_CLONE_VERSION, ret, NULL, NULL); + Request rq(this); + JS_ReadStructuredClone(rq.cx, ptr->m_Data, ptr->m_Size, JS_STRUCTURED_CLONE_VERSION, ret, NULL, NULL); } Index: ps/trunk/source/scriptinterface/tests/test_ScriptConversions.h =================================================================== --- ps/trunk/source/scriptinterface/tests/test_ScriptConversions.h +++ ps/trunk/source/scriptinterface/tests/test_ScriptConversions.h @@ -35,16 +35,15 @@ { ScriptInterface script("Test", "Test", g_ScriptRuntime); TS_ASSERT(script.LoadGlobalScripts()); - JSContext* cx = script.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(script); - JS::RootedValue v1(cx); - ScriptInterface::ToJSVal(cx, &v1, value); + JS::RootedValue v1(rq.cx); + ScriptInterface::ToJSVal(rq, &v1, value); // We want to convert values to strings, but can't just call toSource() on them // since they might not be objects. So just use uneval. std::string source; - JS::RootedValue global(cx, script.GetGlobalObject()); + JS::RootedValue global(rq.cx, script.GetGlobalObject()); TS_ASSERT(script.CallFunction(global, "uneval", source, v1)); TS_ASSERT_STR_EQUALS(source, expected); @@ -55,21 +54,20 @@ { ScriptInterface script("Test", "Test", g_ScriptRuntime); TS_ASSERT(script.LoadGlobalScripts()); - JSContext* cx = script.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(script); - JS::RootedValue v1(cx); - ScriptInterface::ToJSVal(cx, &v1, value); + JS::RootedValue v1(rq.cx); + ScriptInterface::ToJSVal(rq, &v1, value); std::string source; - JS::RootedValue global(cx, script.GetGlobalObject()); + JS::RootedValue global(rq.cx, script.GetGlobalObject()); TS_ASSERT(script.CallFunction(global, "uneval", source, v1)); if (expected) TS_ASSERT_STR_EQUALS(source, expected); T v2 = T(); - TS_ASSERT(ScriptInterface::FromJSVal(cx, v1, v2)); + TS_ASSERT(ScriptInterface::FromJSVal(rq, v1, v2)); TS_ASSERT_EQUALS(value, v2); } @@ -78,22 +76,21 @@ { ScriptInterface script("Test", "Test", g_ScriptRuntime); TS_ASSERT(script.LoadGlobalScripts()); - JSContext* cx = script.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(script); - JS::RootedValue v1(cx); - ScriptInterface::ToJSVal(cx, &v1, v); - JS::RootedValue u1(cx); - ScriptInterface::ToJSVal(cx, &u1, u); + JS::RootedValue v1(rq.cx); + ScriptInterface::ToJSVal(rq, &v1, v); + JS::RootedValue u1(rq.cx); + ScriptInterface::ToJSVal(rq, &u1, u); T r; - JS::RootedValue r1(cx); + JS::RootedValue r1(rq.cx); TS_ASSERT(script.CallFunction(u1, func.c_str(), r, v1)); - ScriptInterface::ToJSVal(cx, &r1, r); + ScriptInterface::ToJSVal(rq, &r1, r); std::string source; - JS::RootedValue global(cx, script.GetGlobalObject()); + JS::RootedValue global(rq.cx, script.GetGlobalObject()); TS_ASSERT(script.CallFunction(global, "uneval", source, r1)); TS_ASSERT_STR_EQUALS(source, expected); @@ -172,26 +169,25 @@ void test_integers() { ScriptInterface script("Test", "Test", g_ScriptRuntime); - JSContext* cx = script.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(script); // using new uninitialized variables each time to be sure the test doesn't succeeed if ToJSVal doesn't touch the value at all. - JS::RootedValue val0(cx), val1(cx), val2(cx), val3(cx), val4(cx), val5(cx), val6(cx), val7(cx), val8(cx); - ScriptInterface::ToJSVal(cx, &val0, 0); - ScriptInterface::ToJSVal(cx, &val1, JSVAL_INT_MAX - 1); - ScriptInterface::ToJSVal(cx, &val2, JSVAL_INT_MAX); - ScriptInterface::ToJSVal(cx, &val3, JSVAL_INT_MIN + 1); - ScriptInterface::ToJSVal(cx, &val4, -(i64)2147483648u); // JSVAL_INT_MIN + JS::RootedValue val0(rq.cx), val1(rq.cx), val2(rq.cx), val3(rq.cx), val4(rq.cx), val5(rq.cx), val6(rq.cx), val7(rq.cx), val8(rq.cx); + ScriptInterface::ToJSVal(rq, &val0, 0); + ScriptInterface::ToJSVal(rq, &val1, JSVAL_INT_MAX - 1); + ScriptInterface::ToJSVal(rq, &val2, JSVAL_INT_MAX); + ScriptInterface::ToJSVal(rq, &val3, JSVAL_INT_MIN + 1); + ScriptInterface::ToJSVal(rq, &val4, -(i64)2147483648u); // JSVAL_INT_MIN TS_ASSERT(val0.isInt32()); TS_ASSERT(val1.isInt32()); TS_ASSERT(val2.isInt32()); TS_ASSERT(val3.isInt32()); TS_ASSERT(val4.isInt32()); - ScriptInterface::ToJSVal(cx, &val5, 0); - ScriptInterface::ToJSVal(cx, &val6, 2147483646u); // JSVAL_INT_MAX-1 - ScriptInterface::ToJSVal(cx, &val7, 2147483647u); // JSVAL_INT_MAX - ScriptInterface::ToJSVal(cx, &val8, 2147483648u); // JSVAL_INT_MAX+1 + ScriptInterface::ToJSVal(rq, &val5, 0); + ScriptInterface::ToJSVal(rq, &val6, 2147483646u); // JSVAL_INT_MAX-1 + ScriptInterface::ToJSVal(rq, &val7, 2147483647u); // JSVAL_INT_MAX + ScriptInterface::ToJSVal(rq, &val8, 2147483648u); // JSVAL_INT_MAX+1 TS_ASSERT(val5.isInt32()); TS_ASSERT(val6.isInt32()); TS_ASSERT(val7.isInt32()); @@ -205,13 +201,12 @@ convert_to(std::numeric_limits::quiet_NaN(), "NaN"); // can't use roundtrip since nan != nan ScriptInterface script("Test", "Test", g_ScriptRuntime); - JSContext* cx = script.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(script); float f = 0; - JS::RootedValue testNANVal(cx); - ScriptInterface::ToJSVal(cx, &testNANVal, NAN); - TS_ASSERT(ScriptInterface::FromJSVal(cx, testNANVal, f)); + JS::RootedValue testNANVal(rq.cx); + ScriptInterface::ToJSVal(rq, &testNANVal, NAN); + TS_ASSERT(ScriptInterface::FromJSVal(rq, testNANVal, f)); TS_ASSERT(isnan(f)); } @@ -257,22 +252,21 @@ // Fancier conversion: we store UTF8 and get UTF16 and vice-versa ScriptInterface script("Test", "Test", g_ScriptRuntime); TS_ASSERT(script.LoadGlobalScripts()); - JSContext* cx = script.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(script); std::string in_utf8("éè!§$-aezi134900°°©©¢¢ÇÇ‘{¶«¡Ç’[å»ÛÁØ"); std::wstring in_utf16(L"éè!§$-aezi134900°°©©¢¢ÇÇ‘{¶«¡Ç’[å»ÛÁØ"); - JS::RootedValue v1(cx); - ScriptInterface::ToJSVal(cx, &v1, in_utf8); + JS::RootedValue v1(rq.cx); + ScriptInterface::ToJSVal(rq, &v1, in_utf8); std::wstring test_out_utf16; - TS_ASSERT(ScriptInterface::FromJSVal(cx, v1, test_out_utf16)); + TS_ASSERT(ScriptInterface::FromJSVal(rq, v1, test_out_utf16)); TS_ASSERT_EQUALS(test_out_utf16, in_utf16); - JS::RootedValue v2(cx); - ScriptInterface::ToJSVal(cx, &v2, in_utf16); + JS::RootedValue v2(rq.cx); + ScriptInterface::ToJSVal(rq, &v2, in_utf16); std::string test_out_utf8; - TS_ASSERT(ScriptInterface::FromJSVal(cx, v2, test_out_utf8)); + TS_ASSERT(ScriptInterface::FromJSVal(rq, v2, test_out_utf8)); TS_ASSERT_EQUALS(test_out_utf8, in_utf8); } }; Index: ps/trunk/source/scriptinterface/tests/test_ScriptInterface.h =================================================================== --- ps/trunk/source/scriptinterface/tests/test_ScriptInterface.h +++ ps/trunk/source/scriptinterface/tests/test_ScriptInterface.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -65,16 +65,14 @@ ScriptInterface script1("Test", "Test", g_ScriptRuntime); ScriptInterface script2("Test", "Test", g_ScriptRuntime); - JSContext* cx1 = script1.GetContext(); - JSAutoRequest rq1(cx1); - JS::RootedValue obj1(cx1); + ScriptInterface::Request rq1(script1); + JS::RootedValue obj1(rq1.cx); TS_ASSERT(script1.Eval("({'x': 123, 'y': [1, 1.5, '2', 'test', undefined, null, true, false]})", &obj1)); { - JSContext* cx2 = script2.GetContext(); - JSAutoRequest rq2(cx2); + ScriptInterface::Request rq2(script2); - JS::RootedValue obj2(cx2, script2.CloneValueFromOtherContext(script1, obj1)); + JS::RootedValue obj2(rq2.cx, script2.CloneValueFromOtherContext(script1, obj1)); std::string source; TS_ASSERT(script2.CallFunction(obj2, "toSource", source)); @@ -88,17 +86,15 @@ ScriptInterface script1("Test", "Test", g_ScriptRuntime); ScriptInterface script2("Test", "Test", g_ScriptRuntime); - JSContext* cx1 = script1.GetContext(); - JSAutoRequest rq1(cx1); + ScriptInterface::Request rq1(script1); - JS::RootedValue obj1(cx1); + JS::RootedValue obj1(rq1.cx); TS_ASSERT(script1.Eval("var s = '?'; var v = ({get x() { return 123 }, 'y': {'w':{get z() { delete v.y; delete v.n; v = null; s += s; return 4 }}}, 'n': 100}); v", &obj1)); { - JSContext* cx2 = script2.GetContext(); - JSAutoRequest rq2(cx2); + ScriptInterface::Request rq2(script2); - JS::RootedValue obj2(cx2, script2.CloneValueFromOtherContext(script1, obj1)); + JS::RootedValue obj2(rq2.cx, script2.CloneValueFromOtherContext(script1, obj1)); std::string source; TS_ASSERT(script2.CallFunction(obj2, "toSource", source)); @@ -111,21 +107,19 @@ ScriptInterface script1("Test", "Test", g_ScriptRuntime); ScriptInterface script2("Test", "Test", g_ScriptRuntime); - JSContext* cx1 = script1.GetContext(); - JSAutoRequest rq1(cx1); + ScriptInterface::Request rq1(script1); - JS::RootedValue obj1(cx1); + JS::RootedValue obj1(rq1.cx); TS_ASSERT(script1.Eval("var x = []; x[0] = x; ({'a': x, 'b': x})", &obj1)); { - JSContext* cx2 = script2.GetContext(); - JSAutoRequest rq(cx2); - JS::RootedValue obj2(cx2, script2.CloneValueFromOtherContext(script1, obj1)); + ScriptInterface::Request rq2(script2); + JS::RootedValue obj2(rq2.cx, script2.CloneValueFromOtherContext(script1, obj1)); // Use JSAPI function to check if the values of the properties "a", "b" are equals a.x[0] - JS::RootedValue prop_a(cx2); - JS::RootedValue prop_b(cx2); - JS::RootedValue prop_x1(cx2); + JS::RootedValue prop_a(rq2.cx); + JS::RootedValue prop_b(rq2.cx); + JS::RootedValue prop_x1(rq2.cx); TS_ASSERT(script2.GetProperty(obj2, "a", &prop_a)); TS_ASSERT(script2.GetProperty(obj2, "b", &prop_b)); TS_ASSERT(prop_a.isObject()); @@ -144,11 +138,10 @@ { ScriptInterface script("Test", "Test", g_ScriptRuntime); - JSContext* cx = script.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(script); - JS::RootedValue val(cx); - JS::RootedValue out(cx); + JS::RootedValue val(rq.cx); + JS::RootedValue out(rq.cx); TS_ASSERT(script.Eval("({ " "'0':0," "inc:function() { this[0]++; return this[0]; }, " @@ -157,7 +150,7 @@ "})" , &val)); - JS::RootedValue nbrVal(cx, JS::NumberValue(3)); + JS::RootedValue nbrVal(rq.cx, JS::NumberValue(3)); int nbr = 0; // CallFunctionVoid JS::RootedValue& parameter overload @@ -166,7 +159,7 @@ // CallFunction JS::RootedValue* out parameter overload script.CallFunction(val, "inc", &out); - ScriptInterface::FromJSVal(cx, out, nbr); + ScriptInterface::FromJSVal(rq, out, nbr); TS_ASSERT_EQUALS(4, nbr); // CallFunction const JS::RootedValue& parameter overload @@ -176,13 +169,13 @@ // GetProperty JS::RootedValue* overload nbr = 0; script.GetProperty(val, "0", &out); - ScriptInterface::FromJSVal(cx, out, nbr); + ScriptInterface::FromJSVal(rq, out, nbr); TS_ASSERT_EQUALS(nbr, 7); // GetPropertyInt JS::RootedValue* overload nbr = 0; script.GetPropertyInt(val, 0, &out); - ScriptInterface::FromJSVal(cx, out, nbr); + ScriptInterface::FromJSVal(rq, out, nbr); TS_ASSERT_EQUALS(nbr, 7); handle_templates_test(script, val, &out, nbrVal); @@ -190,6 +183,8 @@ void handle_templates_test(const ScriptInterface& script, JS::HandleValue val, JS::MutableHandleValue out, JS::HandleValue nbrVal) { + ScriptInterface::Request rq(script); + int nbr = 0; // CallFunctionVoid JS::HandleValue parameter overload @@ -198,7 +193,7 @@ // CallFunction JS::MutableHandleValue out parameter overload script.CallFunction(val, "inc", out); - ScriptInterface::FromJSVal(script.GetContext(), out, nbr); + ScriptInterface::FromJSVal(rq, out, nbr); TS_ASSERT_EQUALS(4, nbr); // CallFunction const JS::HandleValue& parameter overload @@ -208,13 +203,13 @@ // GetProperty JS::MutableHandleValue overload nbr = 0; script.GetProperty(val, "0", out); - ScriptInterface::FromJSVal(script.GetContext(), out, nbr); + ScriptInterface::FromJSVal(rq, out, nbr); TS_ASSERT_EQUALS(nbr, 7); // GetPropertyInt JS::MutableHandleValue overload nbr = 0; script.GetPropertyInt(val, 0, out); - ScriptInterface::FromJSVal(script.GetContext(), out, nbr); + ScriptInterface::FromJSVal(rq, out, nbr); TS_ASSERT_EQUALS(nbr, 7); } @@ -241,11 +236,10 @@ void test_json() { ScriptInterface script("Test", "Test", g_ScriptRuntime); - JSContext* cx = script.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(script); std::string input = "({'x':1,'z':[2,'3\\u263A\\ud800'],\"y\":true})"; - JS::RootedValue val(cx); + JS::RootedValue val(rq.cx); TS_ASSERT(script.Eval(input.c_str(), &val)); std::string stringified = script.StringifyJSON(&val); @@ -260,8 +254,7 @@ void test_function_override() { ScriptInterface script("Test", "Test", g_ScriptRuntime); - JSContext* cx = script.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(script); TS_ASSERT(script.Eval( "function f() { return 1; }" @@ -270,11 +263,11 @@ "})(f);" )); - JS::RootedValue out(cx); + JS::RootedValue out(rq.cx); TS_ASSERT(script.Eval("f()", &out)); int outNbr = 0; - ScriptInterface::FromJSVal(cx, out, outNbr); + ScriptInterface::FromJSVal(rq, out, outNbr); TS_ASSERT_EQUALS(2, outNbr); } }; Index: ps/trunk/source/simulation2/Simulation2.cpp =================================================================== --- ps/trunk/source/simulation2/Simulation2.cpp +++ ps/trunk/source/simulation2/Simulation2.cpp @@ -167,18 +167,15 @@ static std::vector CloneCommandsFromOtherContext(const ScriptInterface& oldScript, const ScriptInterface& newScript, const std::vector& commands) { - JSContext* cxOld = oldScript.GetContext(); - JSAutoRequest rqOld(cxOld); - std::vector newCommands; newCommands.reserve(commands.size()); + + ScriptInterface::Request rqNew(newScript); for (const SimulationCommand& command : commands) { - JSContext* cxNew = newScript.GetContext(); - JSAutoRequest rqNew(cxNew); - JS::RootedValue tmpCommand(cxNew, newScript.CloneValueFromOtherContext(oldScript, command.data)); + JS::RootedValue tmpCommand(rqNew.cx, newScript.CloneValueFromOtherContext(oldScript, command.data)); newScript.FreezeObject(tmpCommand, true); - SimulationCommand cmd(command.player, cxNew, tmpCommand); + SimulationCommand cmd(command.player, rqNew.cx, tmpCommand); newCommands.emplace_back(std::move(cmd)); } return newCommands; @@ -424,9 +421,8 @@ // Load the trigger scripts after we have loaded the simulation. { - JSContext* cx2 = m_SecondaryComponentManager->GetScriptInterface().GetContext(); - JSAutoRequest rq2(cx2); - JS::RootedValue mapSettingsCloned(cx2, + ScriptInterface::Request rq2(m_SecondaryComponentManager->GetScriptInterface()); + JS::RootedValue mapSettingsCloned(rq2.cx, m_SecondaryComponentManager->GetScriptInterface().CloneValueFromOtherContext( scriptInterface, m_MapSettings)); ENSURE(LoadTriggerScripts(*m_SecondaryComponentManager, mapSettingsCloned, m_SecondaryLoadedScripts)); @@ -737,20 +733,18 @@ void CSimulation2::PreInitGame() { - JSContext* cx = GetScriptInterface().GetContext(); - JSAutoRequest rq(cx); - JS::RootedValue global(cx, GetScriptInterface().GetGlobalObject()); + ScriptInterface::Request rq(GetScriptInterface()); + JS::RootedValue global(rq.cx, GetScriptInterface().GetGlobalObject()); GetScriptInterface().CallFunctionVoid(global, "PreInitGame"); } void CSimulation2::InitGame() { - JSContext* cx = GetScriptInterface().GetContext(); - JSAutoRequest rq(cx); - JS::RootedValue global(cx, GetScriptInterface().GetGlobalObject()); + ScriptInterface::Request rq(GetScriptInterface()); + JS::RootedValue global(rq.cx, GetScriptInterface().GetGlobalObject()); - JS::RootedValue settings(cx); - JS::RootedValue tmpInitAttributes(cx, GetInitAttributes()); + JS::RootedValue settings(rq.cx); + JS::RootedValue tmpInitAttributes(rq.cx, GetInitAttributes()); GetScriptInterface().GetProperty(tmpInitAttributes, "settings", &settings); GetScriptInterface().CallFunctionVoid(global, "InitGame", settings); @@ -845,18 +839,16 @@ void CSimulation2::LoadPlayerSettings(bool newPlayers) { - JSContext* cx = GetScriptInterface().GetContext(); - JSAutoRequest rq(cx); - JS::RootedValue global(cx, GetScriptInterface().GetGlobalObject()); + ScriptInterface::Request rq(GetScriptInterface()); + JS::RootedValue global(rq.cx, GetScriptInterface().GetGlobalObject()); GetScriptInterface().CallFunctionVoid(global, "LoadPlayerSettings", m->m_MapSettings, newPlayers); } void CSimulation2::LoadMapSettings() { - JSContext* cx = GetScriptInterface().GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(GetScriptInterface()); - JS::RootedValue global(cx, GetScriptInterface().GetGlobalObject()); + JS::RootedValue global(rq.cx, GetScriptInterface().GetGlobalObject()); // Initialize here instead of in Update() GetScriptInterface().CallFunctionVoid(global, "LoadMapSettings", m->m_MapSettings); @@ -990,14 +982,13 @@ std::string CSimulation2::GetAIData() { const ScriptInterface& scriptInterface = GetScriptInterface(); - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); - JS::RootedValue aiData(cx, ICmpAIManager::GetAIs(scriptInterface)); + ScriptInterface::Request rq(scriptInterface); + JS::RootedValue aiData(rq.cx, ICmpAIManager::GetAIs(scriptInterface)); // Build single JSON string with array of AI data - JS::RootedValue ais(cx); + JS::RootedValue ais(rq.cx); - if (!ScriptInterface::CreateObject(cx, &ais, "AIData", aiData)) + if (!ScriptInterface::CreateObject(rq, &ais, "AIData", aiData)) return std::string(); return scriptInterface.StringifyJSON(&ais); Index: ps/trunk/source/simulation2/components/CCmpAIManager.cpp =================================================================== --- ps/trunk/source/simulation2/components/CCmpAIManager.cpp +++ ps/trunk/source/simulation2/components/CCmpAIManager.cpp @@ -96,11 +96,10 @@ if (!m_Worker.LoadScripts(m_AIName)) return false; - JSContext* cx = m_ScriptInterface->GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(m_ScriptInterface); OsPath path = L"simulation/ai/" + m_AIName + L"/data.json"; - JS::RootedValue metadata(cx); + JS::RootedValue metadata(rq.cx); m_Worker.LoadMetadata(path, &metadata); if (metadata.isUndefined()) { @@ -111,9 +110,9 @@ // Get the constructor name from the metadata std::string moduleName; std::string constructor; - JS::RootedValue objectWithConstructor(cx); // object that should contain the constructor function - JS::RootedValue global(cx, m_ScriptInterface->GetGlobalObject()); - JS::RootedValue ctor(cx); + JS::RootedValue objectWithConstructor(rq.cx); // object that should contain the constructor function + JS::RootedValue global(rq.cx, m_ScriptInterface->GetGlobalObject()); + JS::RootedValue ctor(rq.cx); if (!m_ScriptInterface->HasProperty(metadata, "moduleName")) { LOGERROR("Failed to create AI player: %s: missing 'moduleName'", path.string8()); @@ -145,9 +144,9 @@ m_ScriptInterface->GetProperty(metadata, "useShared", m_UseSharedComponent); // Set up the data to pass as the constructor argument - JS::RootedValue settings(cx); + JS::RootedValue settings(rq.cx); ScriptInterface::CreateObject( - cx, + rq, &settings, "player", m_Player, "difficulty", m_Difficulty, @@ -159,7 +158,7 @@ m_ScriptInterface->SetProperty(settings, "templates", m_Worker.m_EntityTemplates, false); } - JS::AutoValueVector argv(cx); + JS::AutoValueVector argv(rq.cx); argv.append(settings.get()); m_ScriptInterface->CallConstructor(ctor, argv, &m_Obj); @@ -313,18 +312,17 @@ { ENSURE(pCxPrivate->pCBData); CAIWorker* self = static_cast (pCxPrivate->pCBData); - JSContext* cx(self->m_ScriptInterface->GetContext()); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(self->m_ScriptInterface); CFixedVector2D pos, goalPos; std::vector waypoints; - JS::RootedValue retVal(cx); + JS::RootedValue retVal(rq.cx); - self->m_ScriptInterface->FromJSVal(cx, position, pos); - self->m_ScriptInterface->FromJSVal(cx, goal, goalPos); + self->m_ScriptInterface->FromJSVal(rq, position, pos); + self->m_ScriptInterface->FromJSVal(rq, goal, goalPos); self->ComputePath(pos, goalPos, passClass, waypoints); - self->m_ScriptInterface->ToJSVal >(cx, &retVal, waypoints); + self->m_ScriptInterface->ToJSVal >(rq, &retVal, waypoints); return retVal; } @@ -404,8 +402,7 @@ bool TryLoadSharedComponent() { - JSContext* cx = m_ScriptInterface->GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(m_ScriptInterface); // we don't need to load it. if (!m_HasSharedComponent) @@ -424,9 +421,9 @@ // Constructor name is SharedScript, it's in the module API3 // TODO: Hardcoding this is bad, we need a smarter way. - JS::RootedValue AIModule(cx); - JS::RootedValue global(cx, m_ScriptInterface->GetGlobalObject()); - JS::RootedValue ctor(cx); + JS::RootedValue AIModule(rq.cx); + JS::RootedValue global(rq.cx, m_ScriptInterface->GetGlobalObject()); + JS::RootedValue ctor(rq.cx); if (!m_ScriptInterface->GetProperty(global, "API3", &AIModule) || AIModule.isUndefined()) { LOGERROR("Failed to create shared AI component: %s: can't find module '%s'", path.string8(), "API3"); @@ -441,26 +438,26 @@ } // Set up the data to pass as the constructor argument - JS::RootedValue playersID(cx); - ScriptInterface::CreateObject(cx, &playersID); + JS::RootedValue playersID(rq.cx); + ScriptInterface::CreateObject(rq, &playersID); for (size_t i = 0; i < m_Players.size(); ++i) { - JS::RootedValue val(cx); - m_ScriptInterface->ToJSVal(cx, &val, m_Players[i]->m_Player); + JS::RootedValue val(rq.cx); + m_ScriptInterface->ToJSVal(rq, &val, m_Players[i]->m_Player); m_ScriptInterface->SetPropertyInt(playersID, i, val, true); } ENSURE(m_HasLoadedEntityTemplates); - JS::RootedValue settings(cx); + JS::RootedValue settings(rq.cx); ScriptInterface::CreateObject( - cx, + rq, &settings, "players", playersID, "templates", m_EntityTemplates); - JS::AutoValueVector argv(cx); + JS::AutoValueVector argv(rq.cx); argv.append(settings); m_ScriptInterface->CallConstructor(ctor, argv, &m_SharedAIObj); @@ -494,13 +491,12 @@ // this will be run last by InitGame.js, passing the full game representation. // For now it will run for the shared Component. // This is NOT run during deserialization. - JSContext* cx = m_ScriptInterface->GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(m_ScriptInterface); - JS::RootedValue state(cx); + JS::RootedValue state(rq.cx); m_ScriptInterface->ReadStructuredClone(gameState, &state); - ScriptInterface::ToJSVal(cx, &m_PassabilityMapVal, passabilityMap); - ScriptInterface::ToJSVal(cx, &m_TerritoryMapVal, territoryMap); + ScriptInterface::ToJSVal(rq, &m_PassabilityMapVal, passabilityMap); + ScriptInterface::ToJSVal(rq, &m_TerritoryMapVal, territoryMap); m_PassabilityMap = passabilityMap; m_NonPathfindingPassClasses = nonPathfindingPassClassMasks; @@ -549,21 +545,19 @@ m_HierarchicalPathfinder.Update(&m_PassabilityMap, dirtinessGrid); } - JSContext* cx = m_ScriptInterface->GetContext(); + ScriptInterface::Request rq(m_ScriptInterface); if (dimensionChange || justDeserialized) - ScriptInterface::ToJSVal(cx, &m_PassabilityMapVal, m_PassabilityMap); + ScriptInterface::ToJSVal(rq, &m_PassabilityMapVal, m_PassabilityMap); else { // Avoid a useless memory reallocation followed by a garbage collection. - JSAutoRequest rq(cx); - - JS::RootedObject mapObj(cx, &m_PassabilityMapVal.toObject()); - JS::RootedValue mapData(cx); - ENSURE(JS_GetProperty(cx, mapObj, "data", &mapData)); - JS::RootedObject dataObj(cx, &mapData.toObject()); + JS::RootedObject mapObj(rq.cx, &m_PassabilityMapVal.toObject()); + JS::RootedValue mapData(rq.cx); + ENSURE(JS_GetProperty(rq.cx, mapObj, "data", &mapData)); + JS::RootedObject dataObj(rq.cx, &mapData.toObject()); u32 length = 0; - ENSURE(JS_GetArrayLength(cx, dataObj, &length)); + ENSURE(JS_GetArrayLength(rq.cx, dataObj, &length)); u32 nbytes = (u32)(length * sizeof(NavcellData)); bool sharedMemory; @@ -579,21 +573,19 @@ m_TerritoryMap = territoryMap; - JSContext* cx = m_ScriptInterface->GetContext(); + ScriptInterface::Request rq(m_ScriptInterface); if (dimensionChange) - ScriptInterface::ToJSVal(cx, &m_TerritoryMapVal, m_TerritoryMap); + ScriptInterface::ToJSVal(rq, &m_TerritoryMapVal, m_TerritoryMap); else { // Avoid a useless memory reallocation followed by a garbage collection. - JSAutoRequest rq(cx); - - JS::RootedObject mapObj(cx, &m_TerritoryMapVal.toObject()); - JS::RootedValue mapData(cx); - ENSURE(JS_GetProperty(cx, mapObj, "data", &mapData)); - JS::RootedObject dataObj(cx, &mapData.toObject()); + JS::RootedObject mapObj(rq.cx, &m_TerritoryMapVal.toObject()); + JS::RootedValue mapData(rq.cx); + ENSURE(JS_GetProperty(rq.cx, mapObj, "data", &mapData)); + JS::RootedObject dataObj(rq.cx, &mapData.toObject()); u32 length = 0; - ENSURE(JS_GetArrayLength(cx, dataObj, &length)); + ENSURE(JS_GetArrayLength(rq.cx, dataObj, &length)); u32 nbytes = (u32)(length * sizeof(u8)); bool sharedMemory; @@ -631,17 +623,16 @@ void LoadEntityTemplates(const std::vector >& templates) { - JSContext* cx = m_ScriptInterface->GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(m_ScriptInterface); m_HasLoadedEntityTemplates = true; - ScriptInterface::CreateObject(cx, &m_EntityTemplates); + ScriptInterface::CreateObject(rq, &m_EntityTemplates); - JS::RootedValue val(cx); + JS::RootedValue val(rq.cx); for (size_t i = 0; i < templates.size(); ++i) { - templates[i].second->ToJSVal(cx, false, &val); + templates[i].second->ToJSVal(rq, false, &val); m_ScriptInterface->SetProperty(m_EntityTemplates, templates[i].first.c_str(), val, true); } } @@ -668,8 +659,7 @@ if (m_Players.empty()) return; - JSContext* cx = m_ScriptInterface->GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(m_ScriptInterface); std::stringstream rngStream; rngStream << m_RNG; @@ -680,7 +670,7 @@ serializer.Bool("useSharedScript", m_HasSharedComponent); if (m_HasSharedComponent) { - JS::RootedValue sharedData(cx); + JS::RootedValue sharedData(rq.cx); if (!m_ScriptInterface->CallFunction(m_SharedAIObj, "Serialize", &sharedData)) LOGERROR("AI shared script Serialize call failed"); serializer.ScriptVal("sharedData", &sharedData); @@ -695,7 +685,7 @@ serializer.NumberU32_Unbounded("num commands", (u32)m_Players[i]->m_Commands.size()); for (size_t j = 0; j < m_Players[i]->m_Commands.size(); ++j) { - JS::RootedValue val(cx); + JS::RootedValue val(rq.cx); m_ScriptInterface->ReadStructuredClone(m_Players[i]->m_Commands[j], &val); serializer.ScriptVal("command", &val); } @@ -703,7 +693,7 @@ bool hasCustomSerialize = m_ScriptInterface->HasProperty(m_Players[i]->m_Obj, "Serialize"); if (hasCustomSerialize) { - JS::RootedValue scriptData(cx); + JS::RootedValue scriptData(rq.cx); if (!m_ScriptInterface->CallFunction(m_Players[i]->m_Obj, "Serialize", &scriptData)) LOGERROR("AI script Serialize call failed"); serializer.ScriptVal("data", &scriptData); @@ -731,8 +721,7 @@ if (numAis == 0) return; - JSContext* cx = m_ScriptInterface->GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(m_ScriptInterface); ENSURE(m_CommandsComputed); // deserializing while we're still actively computing would be bad @@ -750,7 +739,7 @@ if (m_HasSharedComponent) { TryLoadSharedComponent(); - JS::RootedValue sharedData(cx); + JS::RootedValue sharedData(rq.cx); deserializer.ScriptVal("sharedData", &sharedData); if (!m_ScriptInterface->CallFunctionVoid(m_SharedAIObj, "Deserialize", sharedData)) LOGERROR("AI shared script Deserialize call failed"); @@ -774,7 +763,7 @@ m_Players.back()->m_Commands.reserve(numCommands); for (size_t j = 0; j < numCommands; ++j) { - JS::RootedValue val(cx); + JS::RootedValue val(rq.cx); deserializer.ScriptVal("command", &val); m_Players.back()->m_Commands.push_back(m_ScriptInterface->WriteStructuredClone(val)); } @@ -782,7 +771,7 @@ bool hasCustomDeserialize = m_ScriptInterface->HasProperty(m_Players.back()->m_Obj, "Deserialize"); if (hasCustomDeserialize) { - JS::RootedValue scriptData(cx); + JS::RootedValue scriptData(rq.cx); deserializer.ScriptVal("data", &scriptData); if (m_Players[i]->m_UseSharedComponent) { @@ -844,9 +833,8 @@ void PerformComputation() { // Deserialize the game state, to pass to the AI's HandleMessage - JSContext* cx = m_ScriptInterface->GetContext(); - JSAutoRequest rq(cx); - JS::RootedValue state(cx); + ScriptInterface::Request rq(m_ScriptInterface); + JS::RootedValue state(rq.cx); { PROFILE3("AI compute read state"); m_ScriptInterface->ReadStructuredClone(m_GameState, &state); @@ -998,15 +986,14 @@ virtual void RunGamestateInit() { const ScriptInterface& scriptInterface = GetSimContext().GetScriptInterface(); - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(scriptInterface); CmpPtr cmpAIInterface(GetSystemEntity()); ENSURE(cmpAIInterface); // Get the game state from AIInterface // We flush events from the initialization so we get a clean state now. - JS::RootedValue state(cx); + JS::RootedValue state(rq.cx); cmpAIInterface->GetFullRepresentation(&state, true); // Get the passability data @@ -1037,8 +1024,7 @@ PROFILE("AI setup"); const ScriptInterface& scriptInterface = GetSimContext().GetScriptInterface(); - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(scriptInterface); if (m_Worker.getPlayerSize() == 0) return; @@ -1047,7 +1033,7 @@ ENSURE(cmpAIInterface); // Get the game state from AIInterface - JS::RootedValue state(cx); + JS::RootedValue state(rq.cx); if (m_JustDeserialized) cmpAIInterface->GetFullRepresentation(&state, false); else @@ -1102,9 +1088,8 @@ return; const ScriptInterface& scriptInterface = GetSimContext().GetScriptInterface(); - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); - JS::RootedValue clonedCommandVal(cx); + ScriptInterface::Request rq(scriptInterface); + JS::RootedValue clonedCommandVal(rq.cx); for (size_t i = 0; i < commands.size(); ++i) { @@ -1154,11 +1139,10 @@ return; const ScriptInterface& scriptInterface = GetSimContext().GetScriptInterface(); - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(scriptInterface); - JS::RootedValue classesVal(cx); - ScriptInterface::CreateObject(cx, &classesVal); + JS::RootedValue classesVal(rq.cx); + ScriptInterface::CreateObject(rq, &classesVal); std::map classes; cmpPathfinder->GetPassabilityClasses(classes); Index: ps/trunk/source/simulation2/components/CCmpCommandQueue.cpp =================================================================== --- ps/trunk/source/simulation2/components/CCmpCommandQueue.cpp +++ ps/trunk/source/simulation2/components/CCmpCommandQueue.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -51,8 +51,7 @@ virtual void Serialize(ISerializer& serialize) { - JSContext* cx = GetSimContext().GetScriptInterface().GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(GetSimContext().GetScriptInterface()); serialize.NumberU32_Unbounded("num commands", (u32)m_LocalQueue.size()); for (size_t i = 0; i < m_LocalQueue.size(); ++i) @@ -64,36 +63,32 @@ virtual void Deserialize(const CParamNode& UNUSED(paramNode), IDeserializer& deserialize) { - JSContext* cx = GetSimContext().GetScriptInterface().GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(GetSimContext().GetScriptInterface()); u32 numCmds; deserialize.NumberU32_Unbounded("num commands", numCmds); for (size_t i = 0; i < numCmds; ++i) { i32 player; - JS::RootedValue data(cx); + JS::RootedValue data(rq.cx); deserialize.NumberI32_Unbounded("player", player); deserialize.ScriptVal("data", &data); - m_LocalQueue.emplace_back(SimulationCommand(player, cx, data)); + m_LocalQueue.emplace_back(SimulationCommand(player, rq.cx, data)); } } virtual void PushLocalCommand(player_id_t player, JS::HandleValue cmd) { - JSContext* cx = GetSimContext().GetScriptInterface().GetContext(); - JSAutoRequest rq(cx); - - m_LocalQueue.emplace_back(SimulationCommand(player, cx, cmd)); + ScriptInterface::Request rq(GetSimContext().GetScriptInterface()); + m_LocalQueue.emplace_back(SimulationCommand(player, rq.cx, cmd)); } virtual void PostNetworkCommand(JS::HandleValue cmd1) { - JSContext* cx = GetSimContext().GetScriptInterface().GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(GetSimContext().GetScriptInterface()); // TODO: This is a workaround because we need to pass a MutableHandle to StringifyJSON. - JS::RootedValue cmd(cx, cmd1.get()); + JS::RootedValue cmd(rq.cx, cmd1.get()); PROFILE2_EVENT("post net command"); PROFILE2_ATTR("command: %s", GetSimContext().GetScriptInterface().StringifyJSON(&cmd, false).c_str()); @@ -106,10 +101,9 @@ virtual void FlushTurn(const std::vector& commands) { const ScriptInterface& scriptInterface = GetSimContext().GetScriptInterface(); - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(scriptInterface); - JS::RootedValue global(cx, scriptInterface.GetGlobalObject()); + JS::RootedValue global(rq.cx, scriptInterface.GetGlobalObject()); std::vector localCommands; m_LocalQueue.swap(localCommands); Index: ps/trunk/source/simulation2/components/ICmpAIManager.cpp =================================================================== --- ps/trunk/source/simulation2/components/ICmpAIManager.cpp +++ ps/trunk/source/simulation2/components/ICmpAIManager.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -41,9 +41,8 @@ m_ScriptInterface(scriptInterface), m_AIs(scriptInterface.GetJSRuntime()) { - JSContext* cx = m_ScriptInterface.GetContext(); - JSAutoRequest rq(cx); - m_AIs = JS_NewArrayObject(cx, 0); + ScriptInterface::Request rq(m_ScriptInterface); + m_AIs = JS_NewArrayObject(rq.cx, 0); } void Run() @@ -54,8 +53,7 @@ static Status Callback(const VfsPath& pathname, const CFileInfo& UNUSED(fileInfo), const uintptr_t cbData) { GetAIsHelper* self = (GetAIsHelper*)cbData; - JSContext* cx = self->m_ScriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(self->m_ScriptInterface); // Extract the 3rd component of the path (i.e. the directory after simulation/ai/) fs::wpath components = pathname.string(); @@ -63,16 +61,16 @@ std::advance(it, 2); std::wstring dirname = GetWstringFromWpath(*it); - JS::RootedValue ai(cx); - ScriptInterface::CreateObject(cx, &ai); + JS::RootedValue ai(rq.cx); + ScriptInterface::CreateObject(rq, &ai); - JS::RootedValue data(cx); + JS::RootedValue data(rq.cx); self->m_ScriptInterface.ReadJSONFile(pathname, &data); self->m_ScriptInterface.SetProperty(ai, "id", dirname, true); self->m_ScriptInterface.SetProperty(ai, "data", data, true); u32 length; - JS_GetArrayLength(cx, self->m_AIs, &length); - JS_SetElement(cx, self->m_AIs, length, ai); + JS_GetArrayLength(rq.cx, self->m_AIs, &length); + JS_SetElement(rq.cx, self->m_AIs, length, ai); return INFO::OK; } Index: ps/trunk/source/simulation2/components/ICmpFootprint.cpp =================================================================== --- ps/trunk/source/simulation2/components/ICmpFootprint.cpp +++ ps/trunk/source/simulation2/components/ICmpFootprint.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -30,39 +30,38 @@ entity_pos_t size0, size1, height; GetShape(shape, size0, size1, height); - JSContext* cx = GetSimContext().GetScriptInterface().GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(GetSimContext().GetScriptInterface()); - JS::RootedObject obj(cx, JS_NewPlainObject(cx)); + JS::RootedObject obj(rq.cx, JS_NewPlainObject(rq.cx)); if (!obj) return JS::UndefinedValue(); if (shape == CIRCLE) { - JS::RootedValue ptype(cx); - JS::RootedValue pradius(cx); - JS::RootedValue pheight(cx); - ScriptInterface::ToJSVal(cx, &ptype, "circle"); - ScriptInterface::ToJSVal(cx, &pradius, size0); - ScriptInterface::ToJSVal(cx, &pheight, height); - JS_SetProperty(cx, obj, "type", ptype); - JS_SetProperty(cx, obj, "radius", pradius); - JS_SetProperty(cx, obj, "height", pheight); + JS::RootedValue ptype(rq.cx); + JS::RootedValue pradius(rq.cx); + JS::RootedValue pheight(rq.cx); + ScriptInterface::ToJSVal(rq, &ptype, "circle"); + ScriptInterface::ToJSVal(rq, &pradius, size0); + ScriptInterface::ToJSVal(rq, &pheight, height); + JS_SetProperty(rq.cx, obj, "type", ptype); + JS_SetProperty(rq.cx, obj, "radius", pradius); + JS_SetProperty(rq.cx, obj, "height", pheight); } else { - JS::RootedValue ptype(cx); - JS::RootedValue pwidth(cx); - JS::RootedValue pdepth(cx); - JS::RootedValue pheight(cx); - ScriptInterface::ToJSVal(cx, &ptype, "square"); - ScriptInterface::ToJSVal(cx, &pwidth, size0); - ScriptInterface::ToJSVal(cx, &pdepth, size1); - ScriptInterface::ToJSVal(cx, &pheight, height); - JS_SetProperty(cx, obj, "type", ptype); - JS_SetProperty(cx, obj, "width", pwidth); - JS_SetProperty(cx, obj, "depth", pdepth); - JS_SetProperty(cx, obj, "height", pheight); + JS::RootedValue ptype(rq.cx); + JS::RootedValue pwidth(rq.cx); + JS::RootedValue pdepth(rq.cx); + JS::RootedValue pheight(rq.cx); + ScriptInterface::ToJSVal(rq, &ptype, "square"); + ScriptInterface::ToJSVal(rq, &pwidth, size0); + ScriptInterface::ToJSVal(rq, &pdepth, size1); + ScriptInterface::ToJSVal(rq, &pheight, height); + JS_SetProperty(rq.cx, obj, "type", ptype); + JS_SetProperty(rq.cx, obj, "width", pwidth); + JS_SetProperty(rq.cx, obj, "depth", pdepth); + JS_SetProperty(rq.cx, obj, "height", pheight); } return JS::ObjectValue(*obj); Index: ps/trunk/source/simulation2/components/tests/test_CinemaManager.h =================================================================== --- ps/trunk/source/simulation2/components/tests/test_CinemaManager.h +++ ps/trunk/source/simulation2/components/tests/test_CinemaManager.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -35,8 +35,6 @@ void test_basic() { ComponentTestHelper test(g_ScriptRuntime); - JSContext* cx = test.GetScriptInterface().GetContext(); - JSAutoRequest rq(cx); ICmpCinemaManager* cmp = test.Add(CID_CinemaManager, "", SYSTEM_ENTITY); Index: ps/trunk/source/simulation2/components/tests/test_CommandQueue.h =================================================================== --- ps/trunk/source/simulation2/components/tests/test_CommandQueue.h +++ ps/trunk/source/simulation2/components/tests/test_CommandQueue.h @@ -35,8 +35,7 @@ void test_basic() { ComponentTestHelper test(g_ScriptRuntime); - JSContext* cx = test.GetScriptInterface().GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(test.GetScriptInterface()); std::vector empty; @@ -44,7 +43,7 @@ TS_ASSERT(test.GetScriptInterface().Eval("var cmds = []; function ProcessCommand(player, cmd) { cmds.push([player, cmd]); }")); - JS::RootedValue cmd(cx); + JS::RootedValue cmd(rq.cx); TS_ASSERT(test.GetScriptInterface().Eval("([1,2,3])", &cmd)); cmp->PushLocalCommand(1, cmd); Index: ps/trunk/source/simulation2/scripting/EngineScriptConversions.cpp =================================================================== --- ps/trunk/source/simulation2/scripting/EngineScriptConversions.cpp +++ ps/trunk/source/simulation2/scripting/EngineScriptConversions.cpp @@ -31,12 +31,11 @@ #include "simulation2/system/IComponent.h" #include "simulation2/system/ParamNode.h" -#define FAIL(msg) STMT(JS_ReportError(cx, msg); return false) -#define FAIL_VOID(msg) STMT(JS_ReportError(cx, msg); return) +#define FAIL(msg) STMT(JS_ReportError(rq.cx, msg); return false) +#define FAIL_VOID(msg) STMT(JS_ReportError(rq.cx, msg); return) -template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, IComponent* const& val) +template<> void ScriptInterface::ToJSVal(const Request& rq, JS::MutableHandleValue ret, IComponent* const& val) { - JSAutoRequest rq(cx); if (val == NULL) { ret.setNull(); @@ -44,7 +43,7 @@ } // If this is a scripted component, just return the JS object directly - JS::RootedValue instance(cx, val->GetJSInstance()); + JS::RootedValue instance(rq.cx, val->GetJSInstance()); if (!instance.isNull()) { ret.set(instance); @@ -53,8 +52,8 @@ // Otherwise we need to construct a wrapper object // (TODO: cache wrapper objects?) - JS::RootedObject obj(cx); - if (!val->NewJSObject(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface, &obj)) + JS::RootedObject obj(rq.cx); + if (!val->NewJSObject(*ScriptInterface::GetScriptInterfaceAndCBData(rq.cx)->pScriptInterface, &obj)) { // Report as an error, since scripts really shouldn't try to use unscriptable interfaces LOGERROR("IComponent does not have a scriptable interface"); @@ -66,57 +65,54 @@ ret.setObject(*obj); } -template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, CParamNode const& val) +template<> void ScriptInterface::ToJSVal(const Request& rq, JS::MutableHandleValue ret, CParamNode const& val) { - JSAutoRequest rq(cx); - val.ToJSVal(cx, true, ret); + val.ToJSVal(rq, true, ret); // Prevent modifications to the object, so that it's safe to share between // components and to reconstruct on deserialization if (ret.isObject()) { - JS::RootedObject obj(cx, &ret.toObject()); - JS_DeepFreezeObject(cx, obj); + JS::RootedObject obj(rq.cx, &ret.toObject()); + JS_DeepFreezeObject(rq.cx, obj); } } -template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const CParamNode* const& val) +template<> void ScriptInterface::ToJSVal(const Request& rq, JS::MutableHandleValue ret, const CParamNode* const& val) { if (val) - ToJSVal(cx, ret, *val); + ToJSVal(rq, ret, *val); else ret.setUndefined(); } -template<> bool ScriptInterface::FromJSVal(JSContext* cx, JS::HandleValue v, CColor& out) +template<> bool ScriptInterface::FromJSVal(const Request& rq, JS::HandleValue v, CColor& out) { - JSAutoRequest rq(cx); - if (!v.isObject()) FAIL("CColor has to be an object"); - JS::RootedObject obj(cx, &v.toObject()); + JS::RootedObject obj(rq.cx, &v.toObject()); - JS::RootedValue r(cx); - JS::RootedValue g(cx); - JS::RootedValue b(cx); - JS::RootedValue a(cx); - if (!JS_GetProperty(cx, obj, "r", &r) || !FromJSVal(cx, r, out.r)) + JS::RootedValue r(rq.cx); + JS::RootedValue g(rq.cx); + JS::RootedValue b(rq.cx); + JS::RootedValue a(rq.cx); + if (!JS_GetProperty(rq.cx, obj, "r", &r) || !FromJSVal(rq, r, out.r)) FAIL("Failed to get property CColor.r"); - if (!JS_GetProperty(cx, obj, "g", &g) || !FromJSVal(cx, g, out.g)) + if (!JS_GetProperty(rq.cx, obj, "g", &g) || !FromJSVal(rq, g, out.g)) FAIL("Failed to get property CColor.g"); - if (!JS_GetProperty(cx, obj, "b", &b) || !FromJSVal(cx, b, out.b)) + if (!JS_GetProperty(rq.cx, obj, "b", &b) || !FromJSVal(rq, b, out.b)) FAIL("Failed to get property CColor.b"); - if (!JS_GetProperty(cx, obj, "a", &a) || !FromJSVal(cx, a, out.a)) + if (!JS_GetProperty(rq.cx, obj, "a", &a) || !FromJSVal(rq, a, out.a)) FAIL("Failed to get property CColor.a"); return true; } -template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, CColor const& val) +template<> void ScriptInterface::ToJSVal(const Request& rq, JS::MutableHandleValue ret, CColor const& val) { CreateObject( - cx, + rq, ret, "r", val.r, "g", val.g, @@ -124,11 +120,10 @@ "a", val.a); } -template<> bool ScriptInterface::FromJSVal(JSContext* cx, JS::HandleValue v, fixed& out) +template<> bool ScriptInterface::FromJSVal(const Request& rq, JS::HandleValue v, fixed& out) { - JSAutoRequest rq(cx); double ret; - if (!JS::ToNumber(cx, v, &ret)) + if (!JS::ToNumber(rq.cx, v, &ret)) return false; out = fixed::FromDouble(ret); // double can precisely represent the full range of fixed, so this is a non-lossy conversion @@ -136,93 +131,86 @@ return true; } -template<> void ScriptInterface::ToJSVal(JSContext* UNUSED(cx), JS::MutableHandleValue ret, const fixed& val) +template<> void ScriptInterface::ToJSVal(const Request& UNUSED(rq), JS::MutableHandleValue ret, const fixed& val) { ret.set(JS::NumberValue(val.ToDouble())); } -template<> bool ScriptInterface::FromJSVal(JSContext* cx, JS::HandleValue v, CFixedVector3D& out) +template<> bool ScriptInterface::FromJSVal(const Request& rq, JS::HandleValue v, CFixedVector3D& out) { if (!v.isObject()) return false; // TODO: report type error - JSAutoRequest rq(cx); - JS::RootedObject obj(cx, &v.toObject()); - JS::RootedValue p(cx); + JS::RootedObject obj(rq.cx, &v.toObject()); + JS::RootedValue p(rq.cx); - if (!JS_GetProperty(cx, obj, "x", &p)) return false; // TODO: report type errors - if (!FromJSVal(cx, p, out.X)) return false; + if (!JS_GetProperty(rq.cx, obj, "x", &p)) return false; // TODO: report type errors + if (!FromJSVal(rq, p, out.X)) return false; - if (!JS_GetProperty(cx, obj, "y", &p)) return false; - if (!FromJSVal(cx, p, out.Y)) return false; + if (!JS_GetProperty(rq.cx, obj, "y", &p)) return false; + if (!FromJSVal(rq, p, out.Y)) return false; - if (!JS_GetProperty(cx, obj, "z", &p)) return false; - if (!FromJSVal(cx, p, out.Z)) return false; + if (!JS_GetProperty(rq.cx, obj, "z", &p)) return false; + if (!FromJSVal(rq, p, out.Z)) return false; return true; } -template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const CFixedVector3D& val) +template<> void ScriptInterface::ToJSVal(const Request& rq, JS::MutableHandleValue ret, const CFixedVector3D& val) { - JSAutoRequest rq(cx); - - ScriptInterface::CxPrivate* pCxPrivate = ScriptInterface::GetScriptInterfaceAndCBData(cx); - JS::RootedObject global(cx, &pCxPrivate->pScriptInterface->GetGlobalObject().toObject()); - JS::RootedValue valueVector3D(cx); - if (!JS_GetProperty(cx, global, "Vector3D", &valueVector3D)) + ScriptInterface::CxPrivate* pCxPrivate = ScriptInterface::GetScriptInterfaceAndCBData(rq.cx); + JS::RootedObject global(rq.cx, &pCxPrivate->pScriptInterface->GetGlobalObject().toObject()); + JS::RootedValue valueVector3D(rq.cx); + if (!JS_GetProperty(rq.cx, global, "Vector3D", &valueVector3D)) FAIL_VOID("Failed to get Vector3D constructor"); - JS::AutoValueArray<3> args(cx); + JS::AutoValueArray<3> args(rq.cx); args[0].setNumber(val.X.ToDouble()); args[1].setNumber(val.Y.ToDouble()); args[2].setNumber(val.Z.ToDouble()); - if (!JS::Construct(cx, valueVector3D, args, ret)) + if (!JS::Construct(rq.cx, valueVector3D, args, ret)) FAIL_VOID("Failed to construct Vector3D object"); } -template<> bool ScriptInterface::FromJSVal(JSContext* cx, JS::HandleValue v, CFixedVector2D& out) +template<> bool ScriptInterface::FromJSVal(const Request& rq, JS::HandleValue v, CFixedVector2D& out) { - JSAutoRequest rq(cx); if (!v.isObject()) return false; // TODO: report type error - JS::RootedObject obj(cx, &v.toObject()); + JS::RootedObject obj(rq.cx, &v.toObject()); - JS::RootedValue p(cx); + JS::RootedValue p(rq.cx); - if (!JS_GetProperty(cx, obj, "x", &p)) return false; // TODO: report type errors - if (!FromJSVal(cx, p, out.X)) return false; + if (!JS_GetProperty(rq.cx, obj, "x", &p)) return false; // TODO: report type errors + if (!FromJSVal(rq, p, out.X)) return false; - if (!JS_GetProperty(cx, obj, "y", &p)) return false; - if (!FromJSVal(cx, p, out.Y)) return false; + if (!JS_GetProperty(rq.cx, obj, "y", &p)) return false; + if (!FromJSVal(rq, p, out.Y)) return false; return true; } -template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const CFixedVector2D& val) +template<> void ScriptInterface::ToJSVal(const Request& rq, JS::MutableHandleValue ret, const CFixedVector2D& val) { - JSAutoRequest rq(cx); - - ScriptInterface::CxPrivate* pCxPrivate = ScriptInterface::GetScriptInterfaceAndCBData(cx); - JS::RootedObject global(cx, &pCxPrivate->pScriptInterface->GetGlobalObject().toObject()); - JS::RootedValue valueVector2D(cx); - if (!JS_GetProperty(cx, global, "Vector2D", &valueVector2D)) + ScriptInterface::CxPrivate* pCxPrivate = ScriptInterface::GetScriptInterfaceAndCBData(rq.cx); + JS::RootedObject global(rq.cx, &pCxPrivate->pScriptInterface->GetGlobalObject().toObject()); + JS::RootedValue valueVector2D(rq.cx); + if (!JS_GetProperty(rq.cx, global, "Vector2D", &valueVector2D)) FAIL_VOID("Failed to get Vector2D constructor"); - JS::AutoValueArray<2> args(cx); + JS::AutoValueArray<2> args(rq.cx); args[0].setNumber(val.X.ToDouble()); args[1].setNumber(val.Y.ToDouble()); - if (!JS::Construct(cx, valueVector2D, args, ret)) + if (!JS::Construct(rq.cx, valueVector2D, args, ret)) FAIL_VOID("Failed to construct Vector2D object"); } -template<> void ScriptInterface::ToJSVal >(JSContext* cx, JS::MutableHandleValue ret, const Grid& val) +template<> void ScriptInterface::ToJSVal >(const Request& rq, JS::MutableHandleValue ret, const Grid& val) { - JSAutoRequest rq(cx); u32 length = (u32)(val.m_W * val.m_H); u32 nbytes = (u32)(length * sizeof(u8)); - JS::RootedObject objArr(cx, JS_NewUint8Array(cx, length)); + JS::RootedObject objArr(rq.cx, JS_NewUint8Array(rq.cx, length)); // Copy the array data and then remove the no-GC check to allow further changes to the JS data { JS::AutoCheckCannotGC nogc; @@ -230,21 +218,20 @@ memcpy((void*)JS_GetUint8ArrayData(objArr, &sharedMemory, nogc), val.m_Data, nbytes); } - JS::RootedValue data(cx, JS::ObjectValue(*objArr)); + JS::RootedValue data(rq.cx, JS::ObjectValue(*objArr)); CreateObject( - cx, + rq, ret, "width", val.m_W, "height", val.m_H, "data", data); } -template<> void ScriptInterface::ToJSVal >(JSContext* cx, JS::MutableHandleValue ret, const Grid& val) +template<> void ScriptInterface::ToJSVal >(const Request& rq, JS::MutableHandleValue ret, const Grid& val) { - JSAutoRequest rq(cx); u32 length = (u32)(val.m_W * val.m_H); u32 nbytes = (u32)(length * sizeof(u16)); - JS::RootedObject objArr(cx, JS_NewUint16Array(cx, length)); + JS::RootedObject objArr(rq.cx, JS_NewUint16Array(rq.cx, length)); // Copy the array data and then remove the no-GC check to allow further changes to the JS data { JS::AutoCheckCannotGC nogc; @@ -252,43 +239,41 @@ memcpy((void*)JS_GetUint16ArrayData(objArr, &sharedMemory, nogc), val.m_Data, nbytes); } - JS::RootedValue data(cx, JS::ObjectValue(*objArr)); + JS::RootedValue data(rq.cx, JS::ObjectValue(*objArr)); CreateObject( - cx, + rq, ret, "width", val.m_W, "height", val.m_H, "data", data); } -template<> bool ScriptInterface::FromJSVal(JSContext* cx, JS::HandleValue v, TNSpline& out) +template<> bool ScriptInterface::FromJSVal(const Request& rq, JS::HandleValue v, TNSpline& out) { - JSAutoRequest rq(cx); - if (!v.isObject()) FAIL("Argument must be an object"); - JS::RootedObject obj(cx, &v.toObject()); + JS::RootedObject obj(rq.cx, &v.toObject()); bool isArray; - if (!JS_IsArrayObject(cx, obj, &isArray) || !isArray) + if (!JS_IsArrayObject(rq.cx, obj, &isArray) || !isArray) FAIL("Argument must be an array"); u32 numberOfNodes = 0; - if (!JS_GetArrayLength(cx, obj, &numberOfNodes)) + if (!JS_GetArrayLength(rq.cx, obj, &numberOfNodes)) FAIL("Failed to get array length"); for (u32 i = 0; i < numberOfNodes; ++i) { - JS::RootedValue node(cx); - if (!JS_GetElement(cx, obj, i, &node)) + JS::RootedValue node(rq.cx); + if (!JS_GetElement(rq.cx, obj, i, &node)) FAIL("Failed to read array element"); fixed deltaTime; - if (!FromJSProperty(cx, node, "deltaTime", deltaTime)) + if (!FromJSProperty(rq, node, "deltaTime", deltaTime)) FAIL("Failed to read Spline.deltaTime property"); CFixedVector3D position; - if (!FromJSProperty(cx, node, "position", position)) + if (!FromJSProperty(rq, node, "position", position)) FAIL("Failed to read Spline.position property"); out.AddNode(position, CFixedVector3D(), deltaTime); @@ -300,38 +285,36 @@ return true; } -template<> bool ScriptInterface::FromJSVal(JSContext* cx, JS::HandleValue v, CCinemaPath& out) +template<> bool ScriptInterface::FromJSVal(const Request& rq, JS::HandleValue v, CCinemaPath& out) { - JSAutoRequest rq(cx); - if (!v.isObject()) FAIL("Argument must be an object"); - JS::RootedObject obj(cx, &v.toObject()); + JS::RootedObject obj(rq.cx, &v.toObject()); CCinemaData pathData; TNSpline positionSpline, targetSpline; - if (!FromJSProperty(cx, v, "name", pathData.m_Name)) + if (!FromJSProperty(rq, v, "name", pathData.m_Name)) FAIL("Failed to get CCinemaPath.name property"); - if (!FromJSProperty(cx, v, "orientation", pathData.m_Orientation)) + if (!FromJSProperty(rq, v, "orientation", pathData.m_Orientation)) FAIL("Failed to get CCinemaPath.orientation property"); - if (!FromJSProperty(cx, v, "positionNodes", positionSpline)) + if (!FromJSProperty(rq, v, "positionNodes", positionSpline)) FAIL("Failed to get CCinemaPath.positionNodes property"); - if (pathData.m_Orientation == L"target" && !FromJSProperty(cx, v, "targetNodes", targetSpline)) + if (pathData.m_Orientation == L"target" && !FromJSProperty(rq, v, "targetNodes", targetSpline)) FAIL("Failed to get CCinemaPath.targetNodes property"); // Other properties are not necessary to be defined - if (!FromJSProperty(cx, v, "timescale", pathData.m_Timescale)) + if (!FromJSProperty(rq, v, "timescale", pathData.m_Timescale)) pathData.m_Timescale = fixed::FromInt(1); - if (!FromJSProperty(cx, v, "mode", pathData.m_Mode)) + if (!FromJSProperty(rq, v, "mode", pathData.m_Mode)) pathData.m_Mode = L"ease_inout"; - if (!FromJSProperty(cx, v, "style", pathData.m_Style)) + if (!FromJSProperty(rq, v, "style", pathData.m_Style)) pathData.m_Style = L"default"; out = CCinemaPath(pathData, positionSpline, targetSpline); Index: ps/trunk/source/simulation2/scripting/JSInterface_Simulation.cpp =================================================================== --- ps/trunk/source/simulation2/scripting/JSInterface_Simulation.cpp +++ ps/trunk/source/simulation2/scripting/JSInterface_Simulation.cpp @@ -50,10 +50,9 @@ if (!cmpGuiInterface) return JS::UndefinedValue(); - JSContext* cxSim = sim->GetScriptInterface().GetContext(); - JSAutoRequest rqSim(cxSim); - JS::RootedValue arg(cxSim, sim->GetScriptInterface().CloneValueFromOtherContext(*(pCxPrivate->pScriptInterface), data)); - JS::RootedValue ret(cxSim); + ScriptInterface::Request rqSim(sim->GetScriptInterface()); + JS::RootedValue arg(rqSim.cx, sim->GetScriptInterface().CloneValueFromOtherContext(*(pCxPrivate->pScriptInterface), data)); + JS::RootedValue ret(rqSim.cx); cmpGuiInterface->ScriptCall(g_Game->GetViewedPlayerID(), name, arg, &ret); return pCxPrivate->pScriptInterface->CloneValueFromOtherContext(sim->GetScriptInterface(), ret); @@ -71,9 +70,8 @@ if (!cmpCommandQueue) return; - JSContext* cxSim = sim->GetScriptInterface().GetContext(); - JSAutoRequest rqSim(cxSim); - JS::RootedValue cmd2(cxSim, sim->GetScriptInterface().CloneValueFromOtherContext(*(pCxPrivate->pScriptInterface), cmd)); + ScriptInterface::Request rqSim(sim->GetScriptInterface()); + JS::RootedValue cmd2(rqSim.cx, sim->GetScriptInterface().CloneValueFromOtherContext(*(pCxPrivate->pScriptInterface), cmd)); cmpCommandQueue->PostNetworkCommand(cmd2); } @@ -126,10 +124,9 @@ CSimulation2* sim = g_Game->GetSimulation2(); ENSURE(sim); - JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); - JSAutoRequest rq(cx); - JS::RootedValue edgeList(cx); - ScriptInterface::CreateArray(cx, &edgeList); + ScriptInterface::Request rq(pCxPrivate); + JS::RootedValue edgeList(rq.cx); + ScriptInterface::CreateArray(rq, &edgeList); int edgeListIndex = 0; float distanceThreshold = 10.0f; @@ -163,7 +160,7 @@ for (size_t i = 0; i < corners.size(); ++i) { - JS::RootedValue edge(cx); + JS::RootedValue edge(rq.cx); const CFixedVector2D& corner = corners[i]; const CFixedVector2D& nextCorner = corners[(i + 1) % corners.size()]; @@ -175,7 +172,7 @@ CFixedVector2D normal = -(nextCorner - corner).Perpendicular(); normal.Normalize(); ScriptInterface::CreateObject( - cx, + rq, &edge, "begin", corner, "end", nextCorner, Index: ps/trunk/source/simulation2/scripting/MessageTypeConversions.cpp =================================================================== --- ps/trunk/source/simulation2/scripting/MessageTypeConversions.cpp +++ ps/trunk/source/simulation2/scripting/MessageTypeConversions.cpp @@ -21,34 +21,32 @@ #include "simulation2/MessageTypes.h" #define TOJSVAL_SETUP() \ - JSContext* cx = scriptInterface.GetContext(); \ - JSAutoRequest rq(cx); \ - JS::RootedObject obj(cx, JS_NewPlainObject(cx)); \ + ScriptInterface::Request rq(scriptInterface); \ + JS::RootedObject obj(rq.cx, JS_NewPlainObject(rq.cx)); \ if (!obj) \ return JS::UndefinedValue(); #define SET_MSG_PROPERTY(name) \ do { \ - JS::RootedValue prop(cx);\ - ScriptInterface::ToJSVal(cx, &prop, this->name); \ - if (! JS_SetProperty(cx, obj, #name, prop)) \ + JS::RootedValue prop(rq.cx);\ + ScriptInterface::ToJSVal(rq, &prop, this->name); \ + if (! JS_SetProperty(rq.cx, obj, #name, prop)) \ return JS::UndefinedValue(); \ } while (0); #define FROMJSVAL_SETUP() \ - JSContext* cx = scriptInterface.GetContext(); \ - JSAutoRequest rq(cx); \ + ScriptInterface::Request rq(scriptInterface); \ if (val.isPrimitive()) \ return NULL; \ - JS::RootedObject obj(cx, &val.toObject()); \ - JS::RootedValue prop(cx); + JS::RootedObject obj(rq.cx, &val.toObject()); \ + JS::RootedValue prop(rq.cx); #define GET_MSG_PROPERTY(type, name) \ type name; \ { \ - if (! JS_GetProperty(cx, obj, #name, &prop)) \ + if (! JS_GetProperty(rq.cx, obj, #name, &prop)) \ return NULL; \ - if (! ScriptInterface::FromJSVal(cx, prop, name)) \ + if (! ScriptInterface::FromJSVal(rq, prop, name)) \ return NULL; \ } @@ -273,9 +271,9 @@ JS::Value CMessageMotionUpdate::ToJSVal(const ScriptInterface& scriptInterface) const { TOJSVAL_SETUP(); - JS::RootedValue prop(cx); + JS::RootedValue prop(rq.cx); - if (!JS_SetProperty(cx, obj, UpdateTypeStr[updateType], JS::TrueHandleValue)) + if (!JS_SetProperty(rq.cx, obj, UpdateTypeStr[updateType], JS::TrueHandleValue)) return JS::UndefinedValue(); return JS::ObjectValue(*obj); Index: ps/trunk/source/simulation2/scripting/ScriptComponent.cpp =================================================================== --- ps/trunk/source/simulation2/scripting/ScriptComponent.cpp +++ ps/trunk/source/simulation2/scripting/ScriptComponent.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -26,8 +26,7 @@ m_ScriptInterface(scriptInterface), m_Instance(scriptInterface.GetJSRuntime(), instance) { // Cache the property detection for efficiency - JSContext* cx = m_ScriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(m_ScriptInterface); m_HasCustomSerialize = m_ScriptInterface.HasProperty(m_Instance, "Serialize"); m_HasCustomDeserialize = m_ScriptInterface.HasProperty(m_Instance, "Deserialize"); @@ -35,7 +34,7 @@ m_HasNullSerialize = false; if (m_HasCustomSerialize) { - JS::RootedValue val(cx); + JS::RootedValue val(rq.cx); if (m_ScriptInterface.GetProperty(m_Instance, "Serialize", &val) && val.isNull()) m_HasNullSerialize = true; } @@ -55,12 +54,11 @@ void CComponentTypeScript::HandleMessage(const CMessage& msg, bool global) { - JSContext* cx = m_ScriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(m_ScriptInterface); const char* name = global ? msg.GetScriptGlobalHandlerName() : msg.GetScriptHandlerName(); - JS::RootedValue msgVal(cx, msg.ToJSValCached(m_ScriptInterface)); + JS::RootedValue msgVal(rq.cx, msg.ToJSValCached(m_ScriptInterface)); if (!m_ScriptInterface.CallFunctionVoid(m_Instance, name, msgVal)) LOGERROR("Script message handler %s failed", name); @@ -72,14 +70,13 @@ if (m_HasNullSerialize) return; - JSContext* cx = m_ScriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(m_ScriptInterface); // Support a custom "Serialize" function, which returns a new object that will be // serialized instead of the component itself if (m_HasCustomSerialize) { - JS::RootedValue val(cx); + JS::RootedValue val(rq.cx); if (!m_ScriptInterface.CallFunction(m_Instance, "Serialize", &val)) LOGERROR("Script Serialize call failed"); serialize.ScriptVal("object", &val); @@ -92,8 +89,7 @@ void CComponentTypeScript::Deserialize(const CParamNode& paramNode, IDeserializer& deserialize, entity_id_t ent) { - JSContext* cx = m_ScriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(m_ScriptInterface); m_ScriptInterface.SetProperty(m_Instance, "entity", (int)ent, true, false); m_ScriptInterface.SetProperty(m_Instance, "template", paramNode, true, false); @@ -102,7 +98,7 @@ // instead of automatically adding the deserialized properties onto the object if (m_HasCustomDeserialize) { - JS::RootedValue val(cx); + JS::RootedValue val(rq.cx); // If Serialize = null, we'll still call Deserialize but with undefined argument if (!m_HasNullSerialize) Index: ps/trunk/source/simulation2/serialization/BinarySerializer.cpp =================================================================== --- ps/trunk/source/simulation2/serialization/BinarySerializer.cpp +++ ps/trunk/source/simulation2/serialization/BinarySerializer.cpp @@ -57,18 +57,16 @@ CBinarySerializerScriptImpl::CBinarySerializerScriptImpl(const ScriptInterface& scriptInterface, ISerializer& serializer) : m_ScriptInterface(scriptInterface), m_Serializer(serializer), m_ScriptBackrefsNext(0) { - JSContext* cx = m_ScriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(m_ScriptInterface); - m_ScriptBackrefSymbol.init(cx, JS::NewSymbol(cx, nullptr)); + m_ScriptBackrefSymbol.init(rq.cx, JS::NewSymbol(rq.cx, nullptr)); } void CBinarySerializerScriptImpl::HandleScriptVal(JS::HandleValue val) { - JSContext* cx = m_ScriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(m_ScriptInterface); - switch (JS_TypeOfValue(cx, val)) + switch (JS_TypeOfValue(rq.cx, val)) { case JSTYPE_VOID: { @@ -88,7 +86,7 @@ break; } - JS::RootedObject obj(cx, &val.toObject()); + JS::RootedObject obj(rq.cx, &val.toObject()); // If we've already serialized this object, just output a reference to it i32 tag = GetScriptBackrefTag(obj); @@ -101,7 +99,7 @@ // Arrays are special cases of Object bool isArray; - if (JS_IsArrayObject(cx, obj, &isArray) && isArray) + if (JS_IsArrayObject(rq.cx, obj, &isArray) && isArray) { m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_ARRAY); // TODO: probably should have a more efficient storage format @@ -109,7 +107,7 @@ // Arrays like [1, 2, ] have an 'undefined' at the end which is part of the // length but seemingly isn't enumerated, so store the length explicitly uint length = 0; - if (!JS_GetArrayLength(cx, obj, &length)) + if (!JS_GetArrayLength(rq.cx, obj, &length)) throw PSERROR_Serialize_ScriptError("JS_GetArrayLength failed"); m_Serializer.NumberU32_Unbounded("array length", length); } @@ -124,7 +122,7 @@ bool sharedMemory; // Now handle its array buffer // this may be a backref, since ArrayBuffers can be shared by multiple views - JS::RootedValue bufferVal(cx, JS::ObjectValue(*JS_GetArrayBufferViewBuffer(cx, obj, &sharedMemory))); + JS::RootedValue bufferVal(rq.cx, JS::ObjectValue(*JS_GetArrayBufferViewBuffer(rq.cx, obj, &sharedMemory))); HandleScriptVal(bufferVal); break; } @@ -163,7 +161,7 @@ m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_OBJECT_NUMBER); // Get primitive value double d; - if (!JS::ToNumber(cx, val, &d)) + if (!JS::ToNumber(rq.cx, val, &d)) throw PSERROR_Serialize_ScriptError("JS::ToNumber failed"); m_Serializer.NumberDouble_Unbounded("value", d); break; @@ -173,7 +171,7 @@ // Standard String object m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_OBJECT_STRING); // Get primitive value - JS::RootedString str(cx, JS::ToString(cx, val)); + JS::RootedString str(rq.cx, JS::ToString(rq.cx, val)); if (!str) throw PSERROR_Serialize_ScriptError("JS_ValueToString failed"); ScriptString("value", str); @@ -193,17 +191,17 @@ else if (protokey == JSProto_Map) { m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_OBJECT_MAP); - m_Serializer.NumberU32_Unbounded("map size", JS::MapSize(cx, obj)); + m_Serializer.NumberU32_Unbounded("map size", JS::MapSize(rq.cx, obj)); - JS::RootedValue keyValueIterator(cx); - if (!JS::MapEntries(cx, obj, &keyValueIterator)) + JS::RootedValue keyValueIterator(rq.cx); + if (!JS::MapEntries(rq.cx, obj, &keyValueIterator)) throw PSERROR_Serialize_ScriptError("JS::MapEntries failed"); - JS::ForOfIterator it(cx); + JS::ForOfIterator it(rq.cx); if (!it.init(keyValueIterator)) throw PSERROR_Serialize_ScriptError("JS::ForOfIterator::init failed"); - JS::RootedValue keyValuePair(cx); + JS::RootedValue keyValuePair(rq.cx); bool done; while (true) { @@ -213,11 +211,11 @@ if (done) break; - JS::RootedObject keyValuePairObj(cx, &keyValuePair.toObject()); - JS::RootedValue key(cx); - JS::RootedValue value(cx); - ENSURE(JS_GetElement(cx, keyValuePairObj, 0, &key)); - ENSURE(JS_GetElement(cx, keyValuePairObj, 1, &value)); + JS::RootedObject keyValuePairObj(rq.cx, &keyValuePair.toObject()); + JS::RootedValue key(rq.cx); + JS::RootedValue value(rq.cx); + ENSURE(JS_GetElement(rq.cx, keyValuePairObj, 0, &key)); + ENSURE(JS_GetElement(rq.cx, keyValuePairObj, 1, &value)); HandleScriptVal(key); HandleScriptVal(value); @@ -236,12 +234,12 @@ m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_OBJECT_SET); m_Serializer.NumberU32_Unbounded("set size", setSize); - JS::RootedValue valueIterator(cx); + JS::RootedValue valueIterator(rq.cx); m_ScriptInterface.CallFunction(val, "values", &valueIterator); for (u32 i=0; i ida(cx, JS::IdVector(cx)); - if (!JS_Enumerate(cx, obj, &ida)) + JS::Rooted ida(rq.cx, JS::IdVector(rq.cx)); + if (!JS_Enumerate(rq.cx, obj, &ida)) throw PSERROR_Serialize_ScriptError("JS_Enumerate failed"); m_Serializer.NumberU32_Unbounded("num props", (u32)ida.length()); for (size_t i = 0; i < ida.length(); ++i) { - JS::RootedId id(cx, ida[i]); + JS::RootedId id(rq.cx, ida[i]); - JS::RootedValue idval(cx); - JS::RootedValue propval(cx); + JS::RootedValue idval(rq.cx); + JS::RootedValue propval(rq.cx); // Forbid getters, which might delete values and mess things up. - JS::Rooted desc(cx); - if (!JS_GetPropertyDescriptorById(cx, obj, id, &desc)) + JS::Rooted desc(rq.cx); + if (!JS_GetPropertyDescriptorById(rq.cx, obj, id, &desc)) throw PSERROR_Serialize_ScriptError("JS_GetPropertyDescriptorById failed"); if (desc.hasGetterObject()) throw PSERROR_Serialize_ScriptError("Cannot serialize property getters"); // Get the property name as a string - if (!JS_IdToValue(cx, id, &idval)) + if (!JS_IdToValue(rq.cx, id, &idval)) throw PSERROR_Serialize_ScriptError("JS_IdToValue failed"); - JS::RootedString idstr(cx, JS::ToString(cx, idval)); + JS::RootedString idstr(rq.cx, JS::ToString(rq.cx, idval)); if (!idstr) throw PSERROR_Serialize_ScriptError("JS_ValueToString failed"); ScriptString("prop name", idstr); - if (!JS_GetPropertyById(cx, obj, id, &propval)) + if (!JS_GetPropertyById(rq.cx, obj, id, &propval)) throw PSERROR_Serialize_ScriptError("JS_GetPropertyById failed"); HandleScriptVal(propval); @@ -301,17 +299,17 @@ { // We can't serialise functions, but we can at least name the offender (hopefully) std::wstring funcname(L"(unnamed)"); - JS::RootedFunction func(cx, JS_ValueToFunction(cx, val)); + JS::RootedFunction func(rq.cx, JS_ValueToFunction(rq.cx, val)); if (func) { - JS::RootedString string(cx, JS_GetFunctionId(func)); + JS::RootedString string(rq.cx, JS_GetFunctionId(func)); if (string) { if (JS_StringHasLatin1Chars(string)) { size_t length; JS::AutoCheckCannotGC nogc; - const JS::Latin1Char* ch = JS_GetLatin1StringCharsAndLength(cx, nogc, string, &length); + const JS::Latin1Char* ch = JS_GetLatin1StringCharsAndLength(rq.cx, nogc, string, &length); if (ch && length > 0) funcname.assign(ch, ch + length); } @@ -319,7 +317,7 @@ { size_t length; JS::AutoCheckCannotGC nogc; - const char16_t* ch = JS_GetTwoByteStringCharsAndLength(cx, nogc, string, &length); + const char16_t* ch = JS_GetTwoByteStringCharsAndLength(rq.cx, nogc, string, &length); if (ch && length > 0) funcname.assign(ch, ch + length); } @@ -332,7 +330,7 @@ case JSTYPE_STRING: { m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_STRING); - JS::RootedString stringVal(cx, val.toString()); + JS::RootedString stringVal(rq.cx, val.toString()); ScriptString("string", stringVal); break; } @@ -377,8 +375,7 @@ void CBinarySerializerScriptImpl::ScriptString(const char* name, JS::HandleString string) { - JSContext* cx = m_ScriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(m_ScriptInterface); #if BYTE_ORDER != LITTLE_ENDIAN #error TODO: probably need to convert JS strings to little-endian @@ -391,7 +388,7 @@ m_Serializer.Bool("isLatin1", isLatin1); if (isLatin1) { - const JS::Latin1Char* chars = JS_GetLatin1StringCharsAndLength(cx, nogc, string, &length); + const JS::Latin1Char* chars = JS_GetLatin1StringCharsAndLength(rq.cx, nogc, string, &length); if (!chars) throw PSERROR_Serialize_ScriptError("JS_GetLatin1StringCharsAndLength failed"); m_Serializer.NumberU32_Unbounded("string length", (u32)length); @@ -399,7 +396,7 @@ } else { - const char16_t* chars = JS_GetTwoByteStringCharsAndLength(cx, nogc, string, &length); + const char16_t* chars = JS_GetTwoByteStringCharsAndLength(rq.cx, nogc, string, &length); if (!chars) throw PSERROR_Serialize_ScriptError("JS_GetTwoByteStringCharsAndLength failed"); @@ -418,27 +415,26 @@ // Tags are stored on the object. To avoid overwriting any existing property, // they are saved as a uniquely-named, non-enumerable property (the serializer's unique symbol). - JSContext* cx = m_ScriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(m_ScriptInterface); - JS::RootedValue symbolValue(cx, JS::SymbolValue(m_ScriptBackrefSymbol)); - JS::RootedId symbolId(cx); - ENSURE(JS_ValueToId(cx, symbolValue, &symbolId)); + JS::RootedValue symbolValue(rq.cx, JS::SymbolValue(m_ScriptBackrefSymbol)); + JS::RootedId symbolId(rq.cx); + ENSURE(JS_ValueToId(rq.cx, symbolValue, &symbolId)); - JS::RootedValue tagValue(cx); + JS::RootedValue tagValue(rq.cx); // If it was already there, return the tag bool tagFound; - ENSURE(JS_HasPropertyById(cx, obj, symbolId, &tagFound)); + ENSURE(JS_HasPropertyById(rq.cx, obj, symbolId, &tagFound)); if (tagFound) { - ENSURE(JS_GetPropertyById(cx, obj, symbolId, &tagValue)); + ENSURE(JS_GetPropertyById(rq.cx, obj, symbolId, &tagValue)); ENSURE(tagValue.isInt32()); return tagValue.toInt32(); } tagValue = JS::Int32Value(m_ScriptBackrefsNext); - JS_SetPropertyById(cx, obj, symbolId, tagValue); + JS_SetPropertyById(rq.cx, obj, symbolId, tagValue); ++m_ScriptBackrefsNext; // Return a non-tag number so callers know they need to serialize the object Index: ps/trunk/source/simulation2/serialization/StdDeserializer.cpp =================================================================== --- ps/trunk/source/simulation2/serialization/StdDeserializer.cpp +++ ps/trunk/source/simulation2/serialization/StdDeserializer.cpp @@ -112,8 +112,7 @@ JS::Value CStdDeserializer::ReadScriptVal(const char* UNUSED(name), JS::HandleObject appendParent) { - JSContext* cx = m_ScriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(m_ScriptInterface); uint8_t type; NumberU8_Unbounded("type", type); @@ -128,7 +127,7 @@ case SCRIPT_TYPE_ARRAY: case SCRIPT_TYPE_OBJECT: { - JS::RootedObject obj(cx); + JS::RootedObject obj(rq.cx); if (appendParent) { obj.set(appendParent); @@ -137,11 +136,11 @@ { u32 length; NumberU32_Unbounded("array length", length); - obj.set(JS_NewArrayObject(cx, length)); + obj.set(JS_NewArrayObject(rq.cx, length)); } else // SCRIPT_TYPE_OBJECT { - obj.set(JS_NewPlainObject(cx)); + obj.set(JS_NewPlainObject(rq.cx)); } if (!obj) @@ -159,20 +158,20 @@ { std::vector propname; ReadStringLatin1("prop name", propname); - JS::RootedValue propval(cx, ReadScriptVal("prop value", nullptr)); + JS::RootedValue propval(rq.cx, ReadScriptVal("prop value", nullptr)); utf16string prp(propname.begin(), propname.end());; // TODO: Should ask upstream about getting a variant of JS_SetProperty with a length param. - if (!JS_SetUCProperty(cx, obj, (const char16_t*)prp.data(), prp.length(), propval)) + if (!JS_SetUCProperty(rq.cx, obj, (const char16_t*)prp.data(), prp.length(), propval)) throw PSERROR_Deserialize_ScriptError(); } else { utf16string propname; ReadStringUTF16("prop name", propname); - JS::RootedValue propval(cx, ReadScriptVal("prop value", nullptr)); + JS::RootedValue propval(rq.cx, ReadScriptVal("prop value", nullptr)); - if (!JS_SetUCProperty(cx, obj, (const char16_t*)propname.data(), propname.length(), propval)) + if (!JS_SetUCProperty(rq.cx, obj, (const char16_t*)propname.data(), propname.length(), propval)) throw PSERROR_Deserialize_ScriptError(); } } @@ -181,7 +180,7 @@ } case SCRIPT_TYPE_STRING: { - JS::RootedString str(cx); + JS::RootedString str(rq.cx); ScriptString("string", &str); return JS::StringValue(str); } @@ -195,7 +194,7 @@ { double value; NumberDouble_Unbounded("value", value); - JS::RootedValue rval(cx, JS::NumberValue(value)); + JS::RootedValue rval(rq.cx, JS::NumberValue(value)); if (rval.isNull()) throw PSERROR_Deserialize_ScriptError("JS_NewNumberValue failed"); return rval; @@ -210,7 +209,7 @@ { i32 tag; NumberI32("tag", tag, 0, JSVAL_INT_MAX); - JS::RootedObject obj(cx); + JS::RootedObject obj(rq.cx); GetScriptBackref(tag, &obj); if (!obj) throw PSERROR_Deserialize_ScriptError("Invalid backref tag"); @@ -220,13 +219,13 @@ { double value; NumberDouble_Unbounded("value", value); - JS::RootedValue val(cx, JS::NumberValue(value)); + JS::RootedValue val(rq.cx, JS::NumberValue(value)); - JS::RootedObject ctorobj(cx); - if (!JS_GetClassObject(cx, JSProto_Number, &ctorobj)) + JS::RootedObject ctorobj(rq.cx); + if (!JS_GetClassObject(rq.cx, JSProto_Number, &ctorobj)) throw PSERROR_Deserialize_ScriptError("JS_GetClassObject failed"); - JS::RootedObject obj(cx, JS_New(cx, ctorobj, JS::HandleValueArray(val))); + JS::RootedObject obj(rq.cx, JS_New(rq.cx, ctorobj, JS::HandleValueArray(val))); if (!obj) throw PSERROR_Deserialize_ScriptError("JS_New failed"); AddScriptBackref(obj); @@ -234,17 +233,17 @@ } case SCRIPT_TYPE_OBJECT_STRING: { - JS::RootedString str(cx); + JS::RootedString str(rq.cx); ScriptString("value", &str); if (!str) throw PSERROR_Deserialize_ScriptError(); - JS::RootedValue val(cx, JS::StringValue(str)); + JS::RootedValue val(rq.cx, JS::StringValue(str)); - JS::RootedObject ctorobj(cx); - if (!JS_GetClassObject(cx, JSProto_String, &ctorobj)) + JS::RootedObject ctorobj(rq.cx); + if (!JS_GetClassObject(rq.cx, JSProto_String, &ctorobj)) throw PSERROR_Deserialize_ScriptError("JS_GetClassObject failed"); - JS::RootedObject obj(cx, JS_New(cx, ctorobj, JS::HandleValueArray(val))); + JS::RootedObject obj(rq.cx, JS_New(rq.cx, ctorobj, JS::HandleValueArray(val))); if (!obj) throw PSERROR_Deserialize_ScriptError("JS_New failed"); AddScriptBackref(obj); @@ -254,13 +253,13 @@ { bool value; Bool("value", value); - JS::RootedValue val(cx, JS::BooleanValue(value)); + JS::RootedValue val(rq.cx, JS::BooleanValue(value)); - JS::RootedObject ctorobj(cx); - if (!JS_GetClassObject(cx, JSProto_Boolean, &ctorobj)) + JS::RootedObject ctorobj(rq.cx); + if (!JS_GetClassObject(rq.cx, JSProto_Boolean, &ctorobj)) throw PSERROR_Deserialize_ScriptError("JS_GetClassObject failed"); - JS::RootedObject obj(cx, JS_New(cx, ctorobj, JS::HandleValueArray(val))); + JS::RootedObject obj(rq.cx, JS_New(rq.cx, ctorobj, JS::HandleValueArray(val))); if (!obj) throw PSERROR_Deserialize_ScriptError("JS_New failed"); AddScriptBackref(obj); @@ -275,47 +274,47 @@ NumberU32_Unbounded("length", length); // To match the serializer order, we reserve the typed array's backref tag here - JS::RootedObject arrayObj(cx); + JS::RootedObject arrayObj(rq.cx); AddScriptBackref(arrayObj); // Get buffer object - JS::RootedValue bufferVal(cx, ReadScriptVal("buffer", nullptr)); + JS::RootedValue bufferVal(rq.cx, ReadScriptVal("buffer", nullptr)); if (!bufferVal.isObject()) throw PSERROR_Deserialize_ScriptError(); - JS::RootedObject bufferObj(cx, &bufferVal.toObject()); + JS::RootedObject bufferObj(rq.cx, &bufferVal.toObject()); if (!JS_IsArrayBufferObject(bufferObj)) throw PSERROR_Deserialize_ScriptError("js_IsArrayBuffer failed"); switch(arrayType) { case SCRIPT_TYPED_ARRAY_INT8: - arrayObj = JS_NewInt8ArrayWithBuffer(cx, bufferObj, byteOffset, length); + arrayObj = JS_NewInt8ArrayWithBuffer(rq.cx, bufferObj, byteOffset, length); break; case SCRIPT_TYPED_ARRAY_UINT8: - arrayObj = JS_NewUint8ArrayWithBuffer(cx, bufferObj, byteOffset, length); + arrayObj = JS_NewUint8ArrayWithBuffer(rq.cx, bufferObj, byteOffset, length); break; case SCRIPT_TYPED_ARRAY_INT16: - arrayObj = JS_NewInt16ArrayWithBuffer(cx, bufferObj, byteOffset, length); + arrayObj = JS_NewInt16ArrayWithBuffer(rq.cx, bufferObj, byteOffset, length); break; case SCRIPT_TYPED_ARRAY_UINT16: - arrayObj = JS_NewUint16ArrayWithBuffer(cx, bufferObj, byteOffset, length); + arrayObj = JS_NewUint16ArrayWithBuffer(rq.cx, bufferObj, byteOffset, length); break; case SCRIPT_TYPED_ARRAY_INT32: - arrayObj = JS_NewInt32ArrayWithBuffer(cx, bufferObj, byteOffset, length); + arrayObj = JS_NewInt32ArrayWithBuffer(rq.cx, bufferObj, byteOffset, length); break; case SCRIPT_TYPED_ARRAY_UINT32: - arrayObj = JS_NewUint32ArrayWithBuffer(cx, bufferObj, byteOffset, length); + arrayObj = JS_NewUint32ArrayWithBuffer(rq.cx, bufferObj, byteOffset, length); break; case SCRIPT_TYPED_ARRAY_FLOAT32: - arrayObj = JS_NewFloat32ArrayWithBuffer(cx, bufferObj, byteOffset, length); + arrayObj = JS_NewFloat32ArrayWithBuffer(rq.cx, bufferObj, byteOffset, length); break; case SCRIPT_TYPED_ARRAY_FLOAT64: - arrayObj = JS_NewFloat64ArrayWithBuffer(cx, bufferObj, byteOffset, length); + arrayObj = JS_NewFloat64ArrayWithBuffer(rq.cx, bufferObj, byteOffset, length); break; case SCRIPT_TYPED_ARRAY_UINT8_CLAMPED: - arrayObj = JS_NewUint8ClampedArrayWithBuffer(cx, bufferObj, byteOffset, length); + arrayObj = JS_NewUint8ClampedArrayWithBuffer(rq.cx, bufferObj, byteOffset, length); break; default: throw PSERROR_Deserialize_ScriptError("Failed to deserialize unrecognized typed array view"); @@ -336,14 +335,14 @@ void* contents = malloc(length); ENSURE(contents); RawBytes("buffer data", (u8*)contents, length); - JS::RootedObject bufferObj(cx, JS_NewArrayBufferWithContents(cx, length, contents)); + JS::RootedObject bufferObj(rq.cx, JS_NewArrayBufferWithContents(rq.cx, length, contents)); AddScriptBackref(bufferObj); return JS::ObjectValue(*bufferObj); } case SCRIPT_TYPE_OBJECT_MAP: { - JS::RootedObject obj(cx, JS::NewMapObject(cx)); + JS::RootedObject obj(rq.cx, JS::NewMapObject(rq.cx)); AddScriptBackref(obj); u32 mapSize; @@ -351,19 +350,19 @@ for (u32 i=0; i str; ReadStringLatin1(name, str); - out.set(JS_NewStringCopyN(cx, (const char*)str.data(), str.size())); + out.set(JS_NewStringCopyN(rq.cx, (const char*)str.data(), str.size())); if (!out) throw PSERROR_Deserialize_ScriptError("JS_NewStringCopyN failed"); } @@ -425,7 +423,7 @@ utf16string str; ReadStringUTF16(name, str); - out.set(JS_NewUCStringCopyN(cx, (const char16_t*)str.data(), str.length())); + out.set(JS_NewUCStringCopyN(rq.cx, (const char16_t*)str.data(), str.length())); if (!out) throw PSERROR_Deserialize_ScriptError("JS_NewUCStringCopyN failed"); } @@ -438,12 +436,11 @@ void CStdDeserializer::ScriptObjectAppend(const char* name, JS::HandleValue objVal) { - JSContext* cx = m_ScriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(m_ScriptInterface); if (!objVal.isObject()) throw PSERROR_Deserialize_ScriptError(); - JS::RootedObject obj(cx, &objVal.toObject()); + JS::RootedObject obj(rq.cx, &objVal.toObject()); ReadScriptVal(name, obj); } Index: ps/trunk/source/simulation2/system/ComponentManager.cpp =================================================================== --- ps/trunk/source/simulation2/system/ComponentManager.cpp +++ ps/trunk/source/simulation2/system/ComponentManager.cpp @@ -152,8 +152,7 @@ void CComponentManager::Script_RegisterComponentType_Common(ScriptInterface::CxPrivate* pCxPrivate, int iid, const std::string& cname, JS::HandleValue ctor, bool reRegister, bool systemComponent) { CComponentManager* componentManager = static_cast (pCxPrivate->pCBData); - JSContext* cx = componentManager->m_ScriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(componentManager->m_ScriptInterface); // Find the C++ component that wraps the interface int cidWrapper = componentManager->GetScriptWrapper(iid); @@ -231,7 +230,7 @@ mustReloadComponents = true; } - JS::RootedValue protoVal(cx); + JS::RootedValue protoVal(rq.cx); if (!componentManager->m_ScriptInterface.GetProperty(ctor, "prototype", &protoVal)) { componentManager->m_ScriptInterface.ReportError("Failed to get property 'prototype'"); @@ -255,7 +254,7 @@ ctWrapper.dealloc, cname, schema, - std::unique_ptr(new JS::PersistentRootedValue(cx, ctor)) + std::unique_ptr(new JS::PersistentRootedValue(rq.cx, ctor)) }; componentManager->m_ComponentTypesById[cid] = std::move(ct); @@ -309,7 +308,7 @@ std::map::const_iterator eit = comps.begin(); for (; eit != comps.end(); ++eit) { - JS::RootedValue instance(cx, eit->second->GetJSInstance()); + JS::RootedValue instance(rq.cx, eit->second->GetJSInstance()); if (!instance.isNull()) componentManager->m_ScriptInterface.SetPrototype(instance, protoVal); } @@ -730,8 +729,7 @@ IComponent* CComponentManager::ConstructComponent(CEntityHandle ent, ComponentTypeId cid) { - JSContext* cx = m_ScriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(m_ScriptInterface); std::map::const_iterator it = m_ComponentTypesById.find(cid); if (it == m_ComponentTypesById.end()) @@ -754,7 +752,7 @@ std::map& emap2 = m_ComponentsByTypeId[cid]; // If this is a scripted component, construct the appropriate JS object first - JS::RootedValue obj(cx); + JS::RootedValue obj(rq.cx); if (ct.type == CT_Script) { m_ScriptInterface.CallConstructor(*ct.ctor, JS::HandleValueArray::empty(), &obj); Index: ps/trunk/source/simulation2/system/ParamNode.h =================================================================== --- ps/trunk/source/simulation2/system/ParamNode.h +++ ps/trunk/source/simulation2/system/ParamNode.h @@ -22,7 +22,7 @@ #include "maths/Fixed.h" #include "ps/CStrIntern.h" #include "ps/Errors.h" -#include "scriptinterface/ScriptTypes.h" +#include "scriptinterface/ScriptInterface.h" #include #include @@ -248,7 +248,7 @@ * The cache will be reset if *this* node is modified (e.g. by LoadXML), * but *not* if any child nodes are modified (so don't do that). */ - void ToJSVal(JSContext* cx, bool cacheValue, JS::MutableHandleValue ret) const; + void ToJSVal(const ScriptInterface::Request& rq, bool cacheValue, JS::MutableHandleValue ret) const; /** * Returns the names/nodes of the children of this node, ordered by name @@ -275,7 +275,7 @@ void ResetScriptVal(); - void ConstructJSVal(JSContext* cx, JS::MutableHandleValue ret) const; + void ConstructJSVal(const ScriptInterface::Request& rq, JS::MutableHandleValue ret) const; std::wstring m_Value; ChildrenMap m_Childs; Index: ps/trunk/source/simulation2/system/ParamNode.cpp =================================================================== --- ps/trunk/source/simulation2/system/ParamNode.cpp +++ ps/trunk/source/simulation2/system/ParamNode.cpp @@ -358,7 +358,7 @@ } } -void CParamNode::ToJSVal(JSContext* cx, bool cacheValue, JS::MutableHandleValue ret) const +void CParamNode::ToJSVal(const ScriptInterface::Request& rq, bool cacheValue, JS::MutableHandleValue ret) const { if (cacheValue && m_ScriptVal != NULL) { @@ -366,15 +366,14 @@ return; } - ConstructJSVal(cx, ret); + ConstructJSVal(rq, ret); if (cacheValue) - m_ScriptVal.reset(new JS::PersistentRootedValue(cx, ret)); + m_ScriptVal.reset(new JS::PersistentRootedValue(rq.cx, ret)); } -void CParamNode::ConstructJSVal(JSContext* cx, JS::MutableHandleValue ret) const +void CParamNode::ConstructJSVal(const ScriptInterface::Request& rq, JS::MutableHandleValue ret) const { - JSAutoRequest rq(cx); if (m_Childs.empty()) { // Empty node - map to undefined @@ -386,7 +385,7 @@ // Just a string utf16string text(m_Value.begin(), m_Value.end()); - JS::RootedString str(cx, JS_AtomizeAndPinUCStringN(cx, reinterpret_cast(text.data()), text.length())); + JS::RootedString str(rq.cx, JS_AtomizeAndPinUCStringN(rq.cx, reinterpret_cast(text.data()), text.length())); if (str) { ret.setString(str); @@ -399,18 +398,18 @@ // Got child nodes - convert this node into a hash-table-style object: - JS::RootedObject obj(cx, JS_NewPlainObject(cx)); + JS::RootedObject obj(rq.cx, JS_NewPlainObject(rq.cx)); if (!obj) { ret.setUndefined(); return; // TODO: report error } - JS::RootedValue childVal(cx); + JS::RootedValue childVal(rq.cx); for (std::map::const_iterator it = m_Childs.begin(); it != m_Childs.end(); ++it) { - it->second.ConstructJSVal(cx, &childVal); - if (!JS_SetProperty(cx, obj, it->first.c_str(), childVal)) + it->second.ConstructJSVal(rq, &childVal); + if (!JS_SetProperty(rq.cx, obj, it->first.c_str(), childVal)) { ret.setUndefined(); return; // TODO: report error @@ -421,15 +420,15 @@ if (!m_Value.empty()) { utf16string text(m_Value.begin(), m_Value.end()); - JS::RootedString str(cx, JS_AtomizeAndPinUCStringN(cx, reinterpret_cast(text.data()), text.length())); + JS::RootedString str(rq.cx, JS_AtomizeAndPinUCStringN(rq.cx, reinterpret_cast(text.data()), text.length())); if (!str) { ret.setUndefined(); return; // TODO: report error } - JS::RootedValue childVal(cx, JS::StringValue(str)); - if (!JS_SetProperty(cx, obj, "_string", childVal)) + JS::RootedValue childVal(rq.cx, JS::StringValue(str)); + if (!JS_SetProperty(rq.cx, obj, "_string", childVal)) { ret.setUndefined(); return; // TODO: report error Index: ps/trunk/source/simulation2/system/ReplayTurnManager.cpp =================================================================== --- ps/trunk/source/simulation2/system/ReplayTurnManager.cpp +++ ps/trunk/source/simulation2/system/ReplayTurnManager.cpp @@ -87,19 +87,18 @@ LOGERROR("Replay out of sync on turn %d", turn); const ScriptInterface& scriptInterface = m_Simulation2.GetScriptInterface(); - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(scriptInterface); - JS::AutoValueVector paramData(cx); + JS::AutoValueVector paramData(rq.cx); paramData.append(JS::NumberValue(turn)); - JS::RootedValue hashVal(cx); - scriptInterface.ToJSVal(cx, &hashVal, hash); + JS::RootedValue hashVal(rq.cx); + scriptInterface.ToJSVal(rq, &hashVal, hash); paramData.append(hashVal); - JS::RootedValue expectedHashVal(cx); - scriptInterface.ToJSVal(cx, &expectedHashVal, expectedHash); + JS::RootedValue expectedHashVal(rq.cx); + scriptInterface.ToJSVal(rq, &expectedHashVal, expectedHash); paramData.append(expectedHashVal); g_GUI->SendEventToAll(EventNameReplayOutOfSync, paramData); @@ -111,13 +110,12 @@ m_TurnLength = m_ReplayTurnLengths[turn]; - JSContext* cx = m_Simulation2.GetScriptInterface().GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(m_Simulation2.GetScriptInterface()); // Simulate commands for that turn for (const std::pair& p : m_ReplayCommands[turn]) { - JS::RootedValue command(cx); + JS::RootedValue command(rq.cx); m_Simulation2.GetScriptInterface().ParseJSON(p.second, &command); AddCommand(m_ClientId, p.first, command, m_CurrentTurn + 1); } Index: ps/trunk/source/simulation2/system/TurnManager.cpp =================================================================== --- ps/trunk/source/simulation2/system/TurnManager.cpp +++ ps/trunk/source/simulation2/system/TurnManager.cpp @@ -46,7 +46,7 @@ : m_Simulation2(simulation), m_CurrentTurn(0), m_ReadyTurn(1), m_TurnLength(defaultTurnLength), m_PlayerId(-1), m_ClientId(clientId), m_DeltaSimTime(0), m_HasSyncError(false), m_Replay(replay), m_FinalTurn(std::numeric_limits::max()), m_TimeWarpNumTurns(0), - m_QuickSaveMetadata(m_Simulation2.GetScriptInterface().GetContext()) + m_QuickSaveMetadata(m_Simulation2.GetScriptInterface().GetJSRuntime()) { // When we are on turn n, we schedule new commands for n+2. // We know that all other clients have finished scheduling commands for n (else we couldn't have got here). @@ -240,10 +240,8 @@ m_Simulation2.GetScriptInterface().FreezeObject(data, true); - JSContext* cx = m_Simulation2.GetScriptInterface().GetContext(); - JSAutoRequest rq(cx); - - m_QueuedCommands[turn - (m_CurrentTurn+1)][client].emplace_back(player, cx, data); + ScriptInterface::Request rq(m_Simulation2.GetScriptInterface()); + m_QueuedCommands[turn - (m_CurrentTurn+1)][client].emplace_back(player, rq.cx, data); } void CTurnManager::FinishedAllCommands(u32 turn, u32 turnLength) @@ -306,10 +304,9 @@ m_QuickSaveState = stream.str(); - JSContext* cx = m_Simulation2.GetScriptInterface().GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(m_Simulation2.GetScriptInterface()); - if (JS_StructuredClone(cx, GUIMetadata, &m_QuickSaveMetadata, nullptr, nullptr)) + if (JS_StructuredClone(rq.cx, GUIMetadata, &m_QuickSaveMetadata, nullptr, nullptr)) { // Freeze state to ensure that consectuvie loads don't modify the state m_Simulation2.GetScriptInterface().FreezeObject(m_QuickSaveMetadata, true); @@ -346,18 +343,17 @@ if (!g_GUI) return; - JSContext* cx = m_Simulation2.GetScriptInterface().GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(m_Simulation2.GetScriptInterface()); // Provide a copy, so that GUI components don't have to clone to get mutable objects - JS::RootedValue quickSaveMetadataClone(cx); - if (!JS_StructuredClone(cx, m_QuickSaveMetadata, &quickSaveMetadataClone, nullptr, nullptr)) + JS::RootedValue quickSaveMetadataClone(rq.cx); + if (!JS_StructuredClone(rq.cx, m_QuickSaveMetadata, &quickSaveMetadataClone, nullptr, nullptr)) { LOGERROR("Failed to clone quicksave state!"); return; } - JS::AutoValueArray<1> paramData(cx); + JS::AutoValueArray<1> paramData(rq.cx); paramData[0].set(quickSaveMetadataClone); g_GUI->SendEventToAll(EventNameSavegameLoaded, paramData); Index: ps/trunk/source/simulation2/tests/test_CmpTemplateManager.h =================================================================== --- ps/trunk/source/simulation2/tests/test_CmpTemplateManager.h +++ ps/trunk/source/simulation2/tests/test_CmpTemplateManager.h @@ -99,26 +99,25 @@ TS_ASSERT(tempMan != NULL); tempMan->DisableValidation(); - JSContext* cx = man.GetScriptInterface().GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(man.GetScriptInterface()); // This is testing some bugs in the template JS object caching const CParamNode* inherit1 = tempMan->LoadTemplate(ent2, "inherit1"); - JS::RootedValue val(cx); - ScriptInterface::ToJSVal(cx, &val, inherit1); + JS::RootedValue val(rq.cx); + ScriptInterface::ToJSVal(rq, &val, inherit1); TS_ASSERT_STR_EQUALS(man.GetScriptInterface().ToString(&val), "({Test1A:{'@a':\"a1\", '@b':\"b1\", '@c':\"c1\", d:\"d1\", e:\"e1\", f:\"f1\"}})"); const CParamNode* inherit2 = tempMan->LoadTemplate(ent2, "inherit2"); - ScriptInterface::ToJSVal(cx, &val, inherit2); + ScriptInterface::ToJSVal(rq, &val, inherit2); TS_ASSERT_STR_EQUALS(man.GetScriptInterface().ToString(&val), "({'@parent':\"inherit1\", Test1A:{'@a':\"a2\", '@b':\"b1\", '@c':\"c1\", d:\"d2\", e:\"e1\", f:\"f1\", g:\"g2\"}})"); const CParamNode* actor = tempMan->LoadTemplate(ent2, "actor|example1"); - ScriptInterface::ToJSVal(cx, &val, &actor->GetChild("VisualActor")); + ScriptInterface::ToJSVal(rq, &val, &actor->GetChild("VisualActor")); TS_ASSERT_STR_EQUALS(man.GetScriptInterface().ToString(&val), "({Actor:\"example1\", ActorOnly:(void 0), SilhouetteDisplay:\"false\", SilhouetteOccluder:\"false\", VisibleInAtlasOnly:\"false\"})"); const CParamNode* foundation = tempMan->LoadTemplate(ent2, "foundation|actor|example1"); - ScriptInterface::ToJSVal(cx, &val, &foundation->GetChild("VisualActor")); + ScriptInterface::ToJSVal(rq, &val, &foundation->GetChild("VisualActor")); TS_ASSERT_STR_EQUALS(man.GetScriptInterface().ToString(&val), "({Actor:\"example1\", ActorOnly:(void 0), Foundation:(void 0), SilhouetteDisplay:\"false\", SilhouetteOccluder:\"false\", VisibleInAtlasOnly:\"false\"})"); #define GET_FIRST_ELEMENT(n, templateName) \ @@ -127,7 +126,7 @@ { \ if (it->first[0] == '@') \ continue; \ - ScriptInterface::ToJSVal(cx, &val, it->second); \ + ScriptInterface::ToJSVal(rq, &val, it->second); \ break; \ } Index: ps/trunk/source/simulation2/tests/test_Serializer.h =================================================================== --- ps/trunk/source/simulation2/tests/test_Serializer.h +++ ps/trunk/source/simulation2/tests/test_Serializer.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -292,10 +292,9 @@ void helper_script_roundtrip(const char* msg, const char* input, const char* expected, size_t expstreamlen = 0, const char* expstream = NULL, const char* debug = NULL) { ScriptInterface script("Test", "Test", g_ScriptRuntime); - JSContext* cx = script.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(script); - JS::RootedValue obj(cx); + JS::RootedValue obj(rq.cx); TSM_ASSERT(msg, script.Eval(input, &obj)); if (debug) @@ -318,7 +317,7 @@ CStdDeserializer deserialize(script, stream); - JS::RootedValue newobj(cx); + JS::RootedValue newobj(rq.cx); deserialize.ScriptVal("script", &newobj); // NOTE: Don't use good() here - it fails due to a bug in older libc++ versions TSM_ASSERT(msg, !stream.bad() && !stream.fail()); @@ -755,10 +754,9 @@ void test_script_exceptions() { ScriptInterface script("Test", "Test", g_ScriptRuntime); - JSContext* cx = script.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(script); - JS::RootedValue obj(cx); + JS::RootedValue obj(rq.cx); std::stringstream stream; CStdSerializer serialize(script, stream); @@ -790,10 +788,9 @@ const char* input = "var x = {}; for (var i=0;i<256;++i) x[i]=Math.pow(i, 2); x"; ScriptInterface script("Test", "Test", g_ScriptRuntime); - JSContext* cx = script.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(script); - JS::RootedValue obj(cx); + JS::RootedValue obj(rq.cx); TS_ASSERT(script.Eval(input, &obj)); for (size_t i = 0; i < 256; ++i) @@ -805,7 +802,7 @@ CStdDeserializer deserialize(script, stream); - JS::RootedValue newobj(cx); + JS::RootedValue newobj(rq.cx); deserialize.ScriptVal("script", &newobj); // NOTE: Don't use good() here - it fails due to a bug in older libc++ versions TS_ASSERT(!stream.bad() && !stream.fail()); Index: ps/trunk/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp =================================================================== --- ps/trunk/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp +++ ps/trunk/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp @@ -99,16 +99,15 @@ // Random map const ScriptInterface& scriptInterface = g_Game->GetSimulation2()->GetScriptInterface(); - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(scriptInterface); - JS::RootedValue settings(cx); + JS::RootedValue settings(rq.cx); scriptInterface.ParseJSON(*msg->settings, &settings); scriptInterface.SetProperty(settings, "mapType", "random"); - JS::RootedValue attrs(cx); + JS::RootedValue attrs(rq.cx); ScriptInterface::CreateObject( - cx, + rq, &attrs, "mapType", "random", "script", *msg->filename, @@ -128,30 +127,29 @@ InitGame(); const ScriptInterface& scriptInterface = g_Game->GetSimulation2()->GetScriptInterface(); - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(scriptInterface); // Set up 8-element array of empty objects to satisfy init - JS::RootedValue playerData(cx); - ScriptInterface::CreateArray(cx, &playerData); + JS::RootedValue playerData(rq.cx); + ScriptInterface::CreateArray(rq, &playerData); for (int i = 0; i < 8; ++i) { - JS::RootedValue player(cx); - ScriptInterface::CreateObject(cx, &player); + JS::RootedValue player(rq.cx); + ScriptInterface::CreateObject(rq, &player); scriptInterface.SetPropertyInt(playerData, i, player); } - JS::RootedValue settings(cx); + JS::RootedValue settings(rq.cx); ScriptInterface::CreateObject( - cx, + rq, &settings, "mapType", "scenario", "PlayerData", playerData); - JS::RootedValue attrs(cx); + JS::RootedValue attrs(rq.cx); ScriptInterface::CreateObject( - cx, + rq, &attrs, "mapType", "scenario", "map", "maps/scenarios/_default", @@ -168,17 +166,16 @@ InitGame(); const ScriptInterface& scriptInterface = g_Game->GetSimulation2()->GetScriptInterface(); - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); + ScriptInterface::Request rq(scriptInterface); // Scenario CStrW map = *msg->filename; CStrW mapBase = map.BeforeLast(L".pmp"); // strip the file extension, if any - JS::RootedValue attrs(cx); + JS::RootedValue attrs(rq.cx); ScriptInterface::CreateObject( - cx, + rq, &attrs, "mapType", "scenario", "map", mapBase);