Index: ps/trunk/source/graphics/MapGenerator.h =================================================================== --- ps/trunk/source/graphics/MapGenerator.h +++ ps/trunk/source/graphics/MapGenerator.h @@ -67,7 +67,7 @@ * * @return StructuredClone containing map data */ - shared_ptr GetResults(); + ScriptInterface::StructuredClone GetResults(); private: CMapGeneratorWorker* m_Worker; @@ -110,7 +110,7 @@ * * @return StructuredClone containing map data */ - shared_ptr GetResults(); + ScriptInterface::StructuredClone GetResults(); /** * Set initial seed, callback data. @@ -201,7 +201,7 @@ /** * Result of the mapscript generation including terrain, entities and environment settings. */ - shared_ptr m_MapData; + ScriptInterface::StructuredClone m_MapData; /** * Deterministic random number generator. Index: ps/trunk/source/graphics/MapGenerator.cpp =================================================================== --- ps/trunk/source/graphics/MapGenerator.cpp +++ ps/trunk/source/graphics/MapGenerator.cpp @@ -92,7 +92,7 @@ shared_ptr mapgenContext = ScriptContext::CreateContext(RMS_CONTEXT_SIZE); // Enable the script to be aborted - JS_SetInterruptCallback(mapgenContext->GetJSRuntime(), MapGeneratorInterruptCallback); + JS_AddInterruptCallback(mapgenContext->GetGeneralJSContext(), MapGeneratorInterruptCallback); self->m_ScriptInterface = new ScriptInterface("Engine", "MapGenerator", mapgenContext); @@ -214,7 +214,7 @@ return JS_Now(); } -shared_ptr CMapGeneratorWorker::GetResults() +ScriptInterface::StructuredClone CMapGeneratorWorker::GetResults() { std::lock_guard lock(m_WorkerMutex); return m_MapData; @@ -232,7 +232,7 @@ // Copy results std::lock_guard lock(self->m_WorkerMutex); - self->m_MapData = self->m_ScriptInterface->WriteStructuredClone(data); + self->m_MapData = self->m_ScriptInterface->WriteStructuredClone(data, false); self->m_Progress = 0; } @@ -427,7 +427,7 @@ return m_Worker->GetProgress(); } -shared_ptr CMapGenerator::GetResults() +ScriptInterface::StructuredClone CMapGenerator::GetResults() { return m_Worker->GetResults(); } Index: ps/trunk/source/graphics/MapReader.h =================================================================== --- ps/trunk/source/graphics/MapReader.h +++ ps/trunk/source/graphics/MapReader.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 @@ -52,11 +52,11 @@ ~CMapReader(); // LoadMap: try to load the map from given file; reinitialise the scene to new data if successful - void LoadMap(const VfsPath& pathname, JSRuntime* rt, JS::HandleValue settings, CTerrain*, WaterManager*, SkyManager*, CLightEnv*, CGameView*, + void LoadMap(const VfsPath& pathname, const ScriptContext& cx, JS::HandleValue settings, CTerrain*, WaterManager*, SkyManager*, CLightEnv*, CGameView*, CCinemaManager*, CTriggerManager*, CPostprocManager* pPostproc, CSimulation2*, const CSimContext*, int playerID, bool skipEntities); - void LoadRandomMap(const CStrW& scriptFile, JSRuntime* rt, JS::HandleValue settings, CTerrain*, WaterManager*, SkyManager*, CLightEnv*, CGameView*, CCinemaManager*, CTriggerManager*, CPostprocManager* pPostproc_, CSimulation2*, int playerID); + void LoadRandomMap(const CStrW& scriptFile, const ScriptContext& cx, JS::HandleValue settings, CTerrain*, WaterManager*, SkyManager*, CLightEnv*, CGameView*, CCinemaManager*, CTriggerManager*, CPostprocManager* pPostproc_, CSimulation2*, int playerID); private: // Load script settings for use by scripts Index: ps/trunk/source/graphics/MapReader.cpp =================================================================== --- ps/trunk/source/graphics/MapReader.cpp +++ ps/trunk/source/graphics/MapReader.cpp @@ -39,6 +39,7 @@ #include "renderer/PostprocManager.h" #include "renderer/SkyManager.h" #include "renderer/WaterManager.h" +#include "scriptinterface/ScriptContext.h" #include "simulation2/Simulation2.h" #include "simulation2/components/ICmpCinemaManager.h" #include "simulation2/components/ICmpGarrisonHolder.h" @@ -62,7 +63,7 @@ } // LoadMap: try to load the map from given file; reinitialise the scene to new data if successful -void CMapReader::LoadMap(const VfsPath& pathname, JSRuntime* rt, JS::HandleValue settings, CTerrain *pTerrain_, +void CMapReader::LoadMap(const VfsPath& pathname, const ScriptContext& cx, JS::HandleValue settings, CTerrain *pTerrain_, WaterManager* pWaterMan_, SkyManager* pSkyMan_, CLightEnv *pLightEnv_, CGameView *pGameView_, CCinemaManager* pCinema_, CTriggerManager* pTrigMan_, CPostprocManager* pPostproc_, CSimulation2 *pSimulation2_, const CSimContext* pSimContext_, int playerID_, bool skipEntities) @@ -80,7 +81,7 @@ m_PlayerID = playerID_; m_SkipEntities = skipEntities; m_StartingCameraTarget = INVALID_ENTITY; - m_ScriptSettings.init(rt, settings); + m_ScriptSettings.init(cx.GetGeneralJSContext(), settings); filename_xml = pathname.ChangeExtension(L".xml"); @@ -144,7 +145,7 @@ } // LoadRandomMap: try to load the map data; reinitialise the scene to new data if successful -void CMapReader::LoadRandomMap(const CStrW& scriptFile, JSRuntime* rt, JS::HandleValue settings, CTerrain *pTerrain_, +void CMapReader::LoadRandomMap(const CStrW& scriptFile, const ScriptContext& cx, JS::HandleValue settings, CTerrain *pTerrain_, WaterManager* pWaterMan_, SkyManager* pSkyMan_, CLightEnv *pLightEnv_, CGameView *pGameView_, CCinemaManager* pCinema_, CTriggerManager* pTrigMan_, CPostprocManager* pPostproc_, CSimulation2 *pSimulation2_, int playerID_) @@ -152,7 +153,7 @@ m_ScriptFile = scriptFile; pSimulation2 = pSimulation2_; pSimContext = pSimulation2 ? &pSimulation2->GetSimContext() : NULL; - m_ScriptSettings.init(rt, settings); + m_ScriptSettings.init(cx.GetGeneralJSContext(), settings); pTerrain = pTerrain_; pLightEnv = pLightEnv_; pGameView = pGameView_; @@ -1295,7 +1296,7 @@ else if (progress == 0) { // Finished, get results as StructuredClone object, which must be read to obtain the JS::Value - shared_ptr results = m_MapGen->GetResults(); + ScriptInterface::StructuredClone results = m_MapGen->GetResults(); // Parse data into simulation context JS::RootedValue data(rq.cx); Index: ps/trunk/source/gui/GUIManager.h =================================================================== --- ps/trunk/source/gui/GUIManager.h +++ ps/trunk/source/gui/GUIManager.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 @@ -69,13 +69,13 @@ * user inputs. * If given, the callbackHandler function will be executed once this page is closed. */ - void PushPage(const CStrW& pageName, shared_ptr initData, JS::HandleValue callbackFunc); + void PushPage(const CStrW& pageName, ScriptInterface::StructuredClone initData, JS::HandleValue callbackFunc); /** * Unload the currently active GUI page, and make the previous page active. * (There must be at least two pages when you call this.) */ - void PopPage(shared_ptr args); + void PopPage(ScriptInterface::StructuredClone args); /** * Called when a file has been modified, to hotload changes. @@ -132,7 +132,7 @@ /** * Initializes the data that will be used to create the CGUI page one or multiple times (hotloading). */ - SGUIPage(const CStrW& pageName, const shared_ptr initData); + SGUIPage(const CStrW& pageName, const ScriptInterface::StructuredClone initData); /** * Create the CGUI with it's own ScriptInterface. Deletes the previous CGUI if it existed. @@ -147,11 +147,11 @@ /** * Execute the stored callback function with the given arguments. */ - void PerformCallbackFunction(shared_ptr args); + void PerformCallbackFunction(ScriptInterface::StructuredClone args); CStrW name; std::unordered_set inputs; // for hotloading - shared_ptr initData; // data to be passed to the init() function + ScriptInterface::StructuredClone initData; // data to be passed to the init() function shared_ptr gui; // the actual GUI page /** Index: ps/trunk/source/gui/GUIManager.cpp =================================================================== --- ps/trunk/source/gui/GUIManager.cpp +++ ps/trunk/source/gui/GUIManager.cpp @@ -88,16 +88,16 @@ // The page stack is cleared (including the script context where initData came from), // therefore we have to clone initData. - shared_ptr initDataClone; + ScriptInterface::StructuredClone initDataClone; if (!initData.isUndefined()) - initDataClone = srcScriptInterface->WriteStructuredClone(initData); + initDataClone = srcScriptInterface->WriteStructuredClone(initData, true); m_PageStack.clear(); PushPage(pageName, initDataClone, JS::UndefinedHandleValue); } -void CGUIManager::PushPage(const CStrW& pageName, shared_ptr initData, JS::HandleValue callbackFunction) +void CGUIManager::PushPage(const CStrW& pageName, ScriptInterface::StructuredClone initData, JS::HandleValue callbackFunction) { // Store the callback handler in the current GUI page before opening the new one if (!m_PageStack.empty() && !callbackFunction.isUndefined()) @@ -109,7 +109,7 @@ m_PageStack.back().LoadPage(m_ScriptContext); } -void CGUIManager::PopPage(shared_ptr args) +void CGUIManager::PopPage(ScriptInterface::StructuredClone args) { if (m_PageStack.size() < 2) { @@ -121,7 +121,7 @@ m_PageStack.back().PerformCallbackFunction(args); } -CGUIManager::SGUIPage::SGUIPage(const CStrW& pageName, const shared_ptr initData) +CGUIManager::SGUIPage::SGUIPage(const CStrW& pageName, const ScriptInterface::StructuredClone initData) : name(pageName), initData(initData), inputs(), gui(), callbackFunction() { } @@ -129,7 +129,7 @@ void CGUIManager::SGUIPage::LoadPage(shared_ptr scriptContext) { // If we're hotloading then try to grab some data from the previous page - shared_ptr hotloadData; + ScriptInterface::StructuredClone hotloadData; if (gui) { shared_ptr scriptInterface = gui->GetScriptInterface(); @@ -138,7 +138,7 @@ JS::RootedValue global(rq.cx, rq.globalValue()); JS::RootedValue hotloadDataVal(rq.cx); scriptInterface->CallFunction(global, "getHotloadData", &hotloadDataVal); - hotloadData = scriptInterface->WriteStructuredClone(hotloadDataVal); + hotloadData = scriptInterface->WriteStructuredClone(hotloadDataVal, true); } g_CursorName = g_DefaultCursor; @@ -232,10 +232,10 @@ return; } - callbackFunction = std::make_shared(scriptInterface.GetJSRuntime(), callbackFunc); + callbackFunction = std::make_shared(scriptInterface.GetGeneralJSContext(), callbackFunc); } -void CGUIManager::SGUIPage::PerformCallbackFunction(shared_ptr args) +void CGUIManager::SGUIPage::PerformCallbackFunction(ScriptInterface::StructuredClone args) { if (!callbackFunction) return; Index: ps/trunk/source/gui/ObjectBases/IGUIObject.cpp =================================================================== --- ps/trunk/source/gui/ObjectBases/IGUIObject.cpp +++ ps/trunk/source/gui/ObjectBases/IGUIObject.cpp @@ -76,7 +76,7 @@ delete p.second; if (!m_ScriptHandlers.empty()) - JS_RemoveExtraGCRootsTracer(m_pGUI.GetScriptInterface()->GetJSRuntime(), Trace, this); + JS_RemoveExtraGCRootsTracer(m_pGUI.GetScriptInterface()->GetGeneralJSContext(), Trace, this); // m_Children is deleted along all other GUI Objects in the CGUI destructor } @@ -332,7 +332,7 @@ void IGUIObject::SetScriptHandler(const CStr& eventName, JS::HandleObject Function) { if (m_ScriptHandlers.empty()) - JS_AddExtraGCRootsTracer(m_pGUI.GetScriptInterface()->GetJSRuntime(), Trace, this); + JS_AddExtraGCRootsTracer(m_pGUI.GetScriptInterface()->GetGeneralJSContext(), Trace, this); m_ScriptHandlers[eventName] = JS::Heap(Function); @@ -353,7 +353,7 @@ m_ScriptHandlers.erase(it); if (m_ScriptHandlers.empty()) - JS_RemoveExtraGCRootsTracer(m_pGUI.GetScriptInterface()->GetJSRuntime(), Trace, this); + JS_RemoveExtraGCRootsTracer(m_pGUI.GetScriptInterface()->GetGeneralJSContext(), Trace, this); { auto it = m_pGUI.m_EventIGUIObjects.find(eventName); if (it != m_pGUI.m_EventIGUIObjects.end()) @@ -402,7 +402,7 @@ "y", mousePos.y, "buttons", m_pGUI.GetMouseButtons()); JS::AutoValueVector paramData(rq.cx); - paramData.append(mouse); + (void)paramData.append(mouse); ScriptEvent(eventName, paramData); return msg.skipped ? IN_PASS : IN_HANDLED; 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 @@ -29,7 +29,7 @@ // Functions aren't supported for example! void JSI_GUIManager::PushGuiPage(ScriptInterface::CmptPrivate* pCmptPrivate, const std::wstring& name, JS::HandleValue initData, JS::HandleValue callbackFunction) { - g_GUI->PushPage(name, pCmptPrivate->pScriptInterface->WriteStructuredClone(initData), callbackFunction); + g_GUI->PushPage(name, pCmptPrivate->pScriptInterface->WriteStructuredClone(initData, true), callbackFunction); } void JSI_GUIManager::SwitchGuiPage(ScriptInterface::CmptPrivate* pCmptPrivate, const std::wstring& name, JS::HandleValue initData) @@ -46,7 +46,7 @@ return; } - g_GUI->PopPage(pCmptPrivate->pScriptInterface->WriteStructuredClone(args)); + g_GUI->PopPage(pCmptPrivate->pScriptInterface->WriteStructuredClone(args, true)); } JS::Value JSI_GUIManager::GetGUIObjectByName(ScriptInterface::CmptPrivate* pCmptPrivate, const std::string& name) Index: ps/trunk/source/gui/Scripting/JSInterface_GUISize.h =================================================================== --- ps/trunk/source/gui/Scripting/JSInterface_GUISize.h +++ ps/trunk/source/gui/Scripting/JSInterface_GUISize.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 @@ -24,6 +24,7 @@ namespace JSI_GUISize { extern JSClass JSI_class; + extern JSClassOps JSI_classops; extern JSPropertySpec JSI_props[]; extern JSFunctionSpec JSI_methods[]; 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 @@ -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 @@ -23,7 +23,10 @@ #include "scriptinterface/ScriptInterface.h" JSClass JSI_GUISize::JSI_class = { - "GUISize", 0, + "GUISize", 0, &JSI_GUISize::JSI_classops +}; + +JSClassOps JSI_GUISize::JSI_classops = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, Index: ps/trunk/source/gui/Scripting/JSInterface_IGUIObject.h =================================================================== --- ps/trunk/source/gui/Scripting/JSInterface_IGUIObject.h +++ ps/trunk/source/gui/Scripting/JSInterface_IGUIObject.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 @@ -23,6 +23,7 @@ namespace JSI_IGUIObject { extern JSClass JSI_class; + extern JSClassOps JSI_classops; extern JSFunctionSpec JSI_methods[]; void RegisterScriptClass(ScriptInterface& scriptInterface); 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 @@ -27,7 +27,10 @@ #include "scriptinterface/ScriptInterface.h" JSClass JSI_IGUIObject::JSI_class = { - "GUIObject", JSCLASS_HAS_PRIVATE, + "GUIObject", JSCLASS_HAS_PRIVATE, &JSI_IGUIObject::JSI_classops +}; + +JSClassOps JSI_IGUIObject::JSI_classops = { nullptr, JSI_IGUIObject::deleteProperty, JSI_IGUIObject::getProperty, @@ -172,7 +175,7 @@ } if (e->SettingExists(propName)) - return e->m_Settings[propName]->FromJSVal(rq, 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_USER_DEFINED_ERROR); LOGERROR("Property '%s' does not exist!", propName.c_str()); return result.fail(JSMSG_UNDEFINED_PROP); 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 @@ -66,7 +66,7 @@ JS::RootedValue val(rq.cx); scriptInterface.CreateObject(rq, &val); - std::shared_ptr data = scriptInterface.WriteStructuredClone(JS::NullHandleValue); + ScriptInterface::StructuredClone data = scriptInterface.WriteStructuredClone(JS::NullHandleValue, true); g_GUI->PushPage(L"hotkey/page_hotkey.xml", data, JS::UndefinedHandleValue); // Press 'a'. Index: ps/trunk/source/lobby/XmppClient.cpp =================================================================== --- ps/trunk/source/lobby/XmppClient.cpp +++ ps/trunk/source/lobby/XmppClient.cpp @@ -94,7 +94,7 @@ m_PlayerMapUpdate(false) { if (m_ScriptInterface) - JS_AddExtraGCRootsTracer(m_ScriptInterface->GetJSRuntime(), XmppClient::Trace, this); + JS_AddExtraGCRootsTracer(m_ScriptInterface->GetGeneralJSContext(), XmppClient::Trace, this); // Read lobby configuration from default.cfg std::string sXpartamupp; @@ -192,7 +192,7 @@ glooxwrapper::Tag::free(t); if (m_ScriptInterface) - JS_RemoveExtraGCRootsTracer(m_ScriptInterface->GetJSRuntime(), XmppClient::Trace, this); + JS_RemoveExtraGCRootsTracer(m_ScriptInterface->GetGeneralJSContext(), XmppClient::Trace, this); } void XmppClient::TraceMember(JSTracer* trc) @@ -736,7 +736,7 @@ m_GuiMessageQueue.clear(); // Copy the messages over to the caller script interface. - return scriptInterface.CloneValueFromOtherCompartment(*m_ScriptInterface, messages); + return scriptInterface.CloneValueFromOtherCompartment(*m_ScriptInterface, messages, false); } JS::Value XmppClient::GuiPollHistoricMessages(const ScriptInterface& scriptInterface) @@ -754,7 +754,7 @@ m_ScriptInterface->SetPropertyInt(messages, j++, message); // Copy the messages over to the caller script interface. - return scriptInterface.CloneValueFromOtherCompartment(*m_ScriptInterface, messages); + return scriptInterface.CloneValueFromOtherCompartment(*m_ScriptInterface, messages, false); } /** Index: ps/trunk/source/network/NetClient.cpp =================================================================== --- ps/trunk/source/network/NetClient.cpp +++ ps/trunk/source/network/NetClient.cpp @@ -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().GetJSRuntime()), + m_GameAttributes(game->GetSimulation2()->GetScriptInterface().GetGeneralJSContext()), m_IsLocalClient(isLocalClient), m_LastConnectionCheck(0), m_Rejoin(false) @@ -80,7 +80,7 @@ void* context = this; - JS_AddExtraGCRootsTracer(GetScriptInterface().GetJSRuntime(), CNetClient::Trace, this); + JS_AddExtraGCRootsTracer(GetScriptInterface().GetGeneralJSContext(), CNetClient::Trace, this); // Set up transitions for session AddTransition(NCS_UNCONNECTED, (uint)NMT_CONNECT_COMPLETE, NCS_CONNECT, (void*)&OnConnect, context); @@ -144,7 +144,7 @@ CNetClient::~CNetClient() { DestroyConnection(); - JS_RemoveExtraGCRootsTracer(GetScriptInterface().GetJSRuntime(), CNetClient::Trace, this); + JS_RemoveExtraGCRootsTracer(GetScriptInterface().GetGeneralJSContext(), CNetClient::Trace, this); } void CNetClient::TraceMember(JSTracer *trc) Index: ps/trunk/source/network/NetMessageSim.cpp =================================================================== --- ps/trunk/source/network/NetMessageSim.cpp +++ ps/trunk/source/network/NetMessageSim.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 @@ -110,18 +110,18 @@ }; CSimulationMessage::CSimulationMessage(const ScriptInterface& scriptInterface) : - CNetMessage(NMT_SIMULATION_COMMAND), m_ScriptInterface(scriptInterface), m_Data(scriptInterface.GetJSRuntime()) + CNetMessage(NMT_SIMULATION_COMMAND), m_ScriptInterface(scriptInterface), m_Data(scriptInterface.GetGeneralJSContext()) { } CSimulationMessage::CSimulationMessage(const ScriptInterface& scriptInterface, u32 client, i32 player, u32 turn, JS::HandleValue data) : CNetMessage(NMT_SIMULATION_COMMAND), m_ScriptInterface(scriptInterface), - m_Client(client), m_Player(player), m_Turn(turn), m_Data(scriptInterface.GetJSRuntime(), data) + m_Client(client), m_Player(player), m_Turn(turn), m_Data(scriptInterface.GetGeneralJSContext(), data) { } CSimulationMessage::CSimulationMessage(const CSimulationMessage& orig) : - m_Data(orig.m_ScriptInterface.GetJSRuntime()), + m_Data(orig.m_ScriptInterface.GetGeneralJSContext()), m_Client(orig.m_Client), m_Player(orig.m_Player), m_ScriptInterface(orig.m_ScriptInterface), @@ -185,13 +185,13 @@ CGameSetupMessage::CGameSetupMessage(const ScriptInterface& scriptInterface) : - CNetMessage(NMT_GAME_SETUP), m_ScriptInterface(scriptInterface), m_Data(scriptInterface.GetJSRuntime()) + CNetMessage(NMT_GAME_SETUP), m_ScriptInterface(scriptInterface), m_Data(scriptInterface.GetGeneralJSContext()) { } CGameSetupMessage::CGameSetupMessage(const ScriptInterface& scriptInterface, JS::HandleValue data) : CNetMessage(NMT_GAME_SETUP), m_ScriptInterface(scriptInterface), - m_Data(scriptInterface.GetJSRuntime(), data) + m_Data(scriptInterface.GetGeneralJSContext(), data) { } Index: ps/trunk/source/network/NetServer.cpp =================================================================== --- ps/trunk/source/network/NetServer.cpp +++ ps/trunk/source/network/NetServer.cpp @@ -389,7 +389,7 @@ // We create a new ScriptContext for this network thread, with a single ScriptInterface. shared_ptr netServerContext = ScriptContext::CreateContext(); m_ScriptInterface = new ScriptInterface("Engine", "Net server", netServerContext); - m_GameAttributes.init(m_ScriptInterface->GetJSRuntime(), JS::UndefinedValue()); + m_GameAttributes.init(m_ScriptInterface->GetGeneralJSContext(), JS::UndefinedValue()); while (true) { 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 @@ -163,7 +163,7 @@ ScriptRequest rqNet(g_NetClient->GetScriptInterface()); JS::RootedValue pollNet(rqNet.cx); g_NetClient->GuiPoll(&pollNet); - return pCmptPrivate->pScriptInterface->CloneValueFromOtherCompartment(g_NetClient->GetScriptInterface(), pollNet); + return pCmptPrivate->pScriptInterface->CloneValueFromOtherCompartment(g_NetClient->GetScriptInterface(), pollNet, false); } void JSI_Network::SetNetworkGameAttributes(ScriptInterface::CmptPrivate* pCmptPrivate, JS::HandleValue attribs1) Index: ps/trunk/source/ps/Game.cpp =================================================================== --- ps/trunk/source/ps/Game.cpp +++ ps/trunk/source/ps/Game.cpp @@ -243,7 +243,7 @@ scriptInterface.GetProperty(attribs, "script", scriptFile); scriptInterface.GetProperty(attribs, "settings", &settings); - m_World->RegisterInitRMS(scriptFile, scriptInterface.GetJSRuntime(), settings, m_PlayerID); + m_World->RegisterInitRMS(scriptFile, *scriptInterface.GetContext(), settings, m_PlayerID); } else { @@ -252,7 +252,7 @@ scriptInterface.GetProperty(attribs, "map", mapFile); scriptInterface.GetProperty(attribs, "settings", &settings); - m_World->RegisterInit(mapFile, scriptInterface.GetJSRuntime(), settings, m_PlayerID); + m_World->RegisterInit(mapFile, *scriptInterface.GetContext(), settings, m_PlayerID); } if (m_GameView) RegMemFun(g_Renderer.GetSingletonPtr()->GetWaterManager(), &WaterManager::LoadWaterTextures, L"LoadWaterTextures", 80); Index: ps/trunk/source/ps/Mod.cpp =================================================================== --- ps/trunk/source/ps/Mod.cpp +++ ps/trunk/source/ps/Mod.cpp @@ -28,7 +28,6 @@ #include "ps/GameSetup/GameSetup.h" #include "ps/GameSetup/Paths.h" #include "ps/Pyrogenesis.h" -#include "scriptinterface/ScriptContext.h" #include "scriptinterface/ScriptInterface.h" std::vector g_modsLoaded; Index: ps/trunk/source/ps/ProfileViewer.cpp =================================================================== --- ps/trunk/source/ps/ProfileViewer.cpp +++ ps/trunk/source/ps/ProfileViewer.cpp @@ -488,7 +488,7 @@ const ScriptInterface& m_ScriptInterface; JS::PersistentRooted m_Root; DumpTable(const ScriptInterface& scriptInterface, JS::HandleValue root) : - m_ScriptInterface(scriptInterface), m_Root(scriptInterface.GetJSRuntime(), root) + m_ScriptInterface(scriptInterface), m_Root(scriptInterface.GetGeneralJSContext(), root) { } @@ -496,7 +496,7 @@ // automatic move constructor DumpTable(DumpTable && original) : m_ScriptInterface(original.m_ScriptInterface), - m_Root(original.m_ScriptInterface.GetJSRuntime(), original.m_Root.get()) + m_Root(original.m_ScriptInterface.GetGeneralJSContext(), original.m_Root.get()) { } Index: ps/trunk/source/ps/SavedGame.h =================================================================== --- ps/trunk/source/ps/SavedGame.h +++ ps/trunk/source/ps/SavedGame.h @@ -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 @@ -45,7 +45,7 @@ * @param guiMetadataClone if not NULL, store some UI-related data with the saved game * @return INFO::OK if successfully saved, else an error Status */ - Status Save(const CStrW& name, const CStrW& description, CSimulation2& simulation, const shared_ptr& guiMetadataClone); + Status Save(const CStrW& name, const CStrW& description, CSimulation2& simulation, const ScriptInterface::StructuredClone& guiMetadataClone); /** * Create new saved game archive with given prefix and simulation data @@ -56,7 +56,7 @@ * @param guiMetadataClone if not NULL, store some UI-related data with the saved game * @return INFO::OK if successfully saved, else an error Status */ - Status SavePrefix(const CStrW& prefix, const CStrW& description, CSimulation2& simulation, const shared_ptr& guiMetadataClone); + Status SavePrefix(const CStrW& prefix, const CStrW& description, CSimulation2& simulation, const ScriptInterface::StructuredClone& guiMetadataClone); /** * Load saved game archive with the given name Index: ps/trunk/source/ps/SavedGame.cpp =================================================================== --- ps/trunk/source/ps/SavedGame.cpp +++ ps/trunk/source/ps/SavedGame.cpp @@ -35,7 +35,7 @@ // TODO: we ought to check version numbers when loading files -Status SavedGames::SavePrefix(const CStrW& prefix, const CStrW& description, CSimulation2& simulation, const shared_ptr& guiMetadataClone) +Status SavedGames::SavePrefix(const CStrW& prefix, const CStrW& description, CSimulation2& simulation, const ScriptInterface::StructuredClone& guiMetadataClone) { // Determine the filename to save under const VfsPath basenameFormat(L"saves/" + prefix + L"-%04d"); @@ -50,7 +50,7 @@ return Save(filename.Filename().string(), description, simulation, guiMetadataClone); } -Status SavedGames::Save(const CStrW& name, const CStrW& description, CSimulation2& simulation, const shared_ptr& guiMetadataClone) +Status SavedGames::Save(const CStrW& name, const CStrW& description, CSimulation2& simulation, const ScriptInterface::StructuredClone& guiMetadataClone) { ScriptRequest rq(simulation.GetScriptInterface()); @@ -161,7 +161,7 @@ */ CGameLoader(const ScriptInterface& scriptInterface, std::string* savedState) : m_ScriptInterface(scriptInterface), - m_Metadata(scriptInterface.GetJSRuntime()), + m_Metadata(scriptInterface.GetGeneralJSContext()), m_SavedState(savedState) { } Index: ps/trunk/source/ps/World.h =================================================================== --- ps/trunk/source/ps/World.h +++ ps/trunk/source/ps/World.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 @@ -71,12 +71,12 @@ /* Initialize the World - load the map and all objects */ - void RegisterInit(const CStrW& mapFile, JSRuntime* rt, JS::HandleValue settings, int playerID); + void RegisterInit(const CStrW& mapFile, const ScriptContext& cx, JS::HandleValue settings, int playerID); /* Initialize the World - generate and load the random map */ - void RegisterInitRMS(const CStrW& scriptFile, JSRuntime* rt, JS::HandleValue settings, int playerID); + void RegisterInitRMS(const CStrW& scriptFile, const ScriptContext& cx, JS::HandleValue settings, int playerID); /** * Explicitly delete m_MapReader once the map has finished loading. Index: ps/trunk/source/ps/World.cpp =================================================================== --- ps/trunk/source/ps/World.cpp +++ ps/trunk/source/ps/World.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 @@ -65,7 +65,7 @@ /** * Initializes the game world with the attributes provided. **/ -void CWorld::RegisterInit(const CStrW& mapFile, JSRuntime* rt, JS::HandleValue settings, int playerID) +void CWorld::RegisterInit(const CStrW& mapFile, const ScriptContext& cx, JS::HandleValue settings, int playerID) { // Load the map, if one was specified if (mapFile.length()) @@ -75,7 +75,7 @@ try { CTriggerManager* pTriggerManager = NULL; - m_MapReader->LoadMap(mapfilename, rt, settings, m_Terrain, + m_MapReader->LoadMap(mapfilename, cx, settings, m_Terrain, CRenderer::IsInitialised() ? g_Renderer.GetWaterManager() : NULL, CRenderer::IsInitialised() ? g_Renderer.GetSkyManager() : NULL, &g_LightEnv, m_pGame->GetView(), @@ -94,11 +94,11 @@ } } -void CWorld::RegisterInitRMS(const CStrW& scriptFile, JSRuntime* rt, JS::HandleValue settings, int playerID) +void CWorld::RegisterInitRMS(const CStrW& scriptFile, const ScriptContext& cx, JS::HandleValue settings, int playerID) { // If scriptFile is empty, a blank map will be generated using settings (no RMS run) CTriggerManager* pTriggerManager = NULL; - m_MapReader->LoadRandomMap(scriptFile, rt, settings, m_Terrain, + m_MapReader->LoadRandomMap(scriptFile, cx, settings, m_Terrain, CRenderer::IsInitialised() ? g_Renderer.GetWaterManager() : NULL, CRenderer::IsInitialised() ? g_Renderer.GetSkyManager() : NULL, &g_LightEnv, m_pGame->GetView(), 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 @@ -51,7 +51,7 @@ ScriptRequest rqSim(sim->GetScriptInterface()); JS::RootedValue gameAttribs(rqSim.cx, - sim->GetScriptInterface().CloneValueFromOtherCompartment(*(pCmptPrivate->pScriptInterface), attribs)); + sim->GetScriptInterface().CloneValueFromOtherCompartment(*(pCmptPrivate->pScriptInterface), attribs, false)); g_Game->SetPlayerID(playerID); g_Game->StartGame(&gameAttribs, ""); 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 @@ -40,14 +40,14 @@ void JSI_SavedGame::SaveGame(ScriptInterface::CmptPrivate* pCmptPrivate, const std::wstring& filename, const std::wstring& description, JS::HandleValue GUIMetadata) { - shared_ptr GUIMetadataClone = pCmptPrivate->pScriptInterface->WriteStructuredClone(GUIMetadata); + ScriptInterface::StructuredClone GUIMetadataClone = pCmptPrivate->pScriptInterface->WriteStructuredClone(GUIMetadata, false); if (SavedGames::Save(filename, description, *g_Game->GetSimulation2(), GUIMetadataClone) < 0) LOGERROR("Failed to save game"); } void JSI_SavedGame::SaveGamePrefix(ScriptInterface::CmptPrivate* pCmptPrivate, const std::wstring& prefix, const std::wstring& description, JS::HandleValue GUIMetadata) { - shared_ptr GUIMetadataClone = pCmptPrivate->pScriptInterface->WriteStructuredClone(GUIMetadata); + ScriptInterface::StructuredClone GUIMetadataClone = pCmptPrivate->pScriptInterface->WriteStructuredClone(GUIMetadata, false); if (SavedGames::SavePrefix(prefix, description, *g_Game->GetSimulation2(), GUIMetadataClone) < 0) LOGERROR("Failed to save game"); } @@ -99,7 +99,7 @@ ScriptRequest rqGame(sim->GetScriptInterface()); JS::RootedValue gameContextMetadata(rqGame.cx, - sim->GetScriptInterface().CloneValueFromOtherCompartment(*(pCmptPrivate->pScriptInterface), guiContextMetadata)); + sim->GetScriptInterface().CloneValueFromOtherCompartment(*(pCmptPrivate->pScriptInterface), guiContextMetadata, false)); JS::RootedValue gameInitAttributes(rqGame.cx); sim->GetScriptInterface().GetProperty(gameContextMetadata, "initAttributes", &gameInitAttributes); 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 @@ -54,7 +54,7 @@ BuildDirEntListState(ScriptInterface* scriptInterface) : pScriptInterface(scriptInterface), - filename_array(scriptInterface->GetJSRuntime()), + filename_array(scriptInterface->GetGeneralJSContext()), cur_idx(0) { ScriptRequest rq(pScriptInterface); Index: ps/trunk/source/scriptinterface/NativeWrapperDefns.h =================================================================== --- ps/trunk/source/scriptinterface/NativeWrapperDefns.h +++ ps/trunk/source/scriptinterface/NativeWrapperDefns.h @@ -180,7 +180,7 @@ ScriptRequest rq(this); JS::RootedValue jsRet(rq.cx); JS::AutoValueVector argv(rq.cx); - argv.resize(sizeof...(Ts)); + (void)argv.resize(sizeof...(Ts)); AssignOrToJSValHelper<0>(rq, argv, params...); if (!CallFunction_(val, name, argv, &jsRet)) return false; @@ -193,7 +193,7 @@ ScriptRequest rq(this); JS::MutableHandle jsRet(ret); JS::AutoValueVector argv(rq.cx); - argv.resize(sizeof...(Ts)); + (void)argv.resize(sizeof...(Ts)); AssignOrToJSValHelper<0>(rq, argv, params...); return CallFunction_(val, name, argv, jsRet); } @@ -203,7 +203,7 @@ { ScriptRequest rq(this); JS::AutoValueVector argv(rq.cx); - argv.resize(sizeof...(Ts)); + (void)argv.resize(sizeof...(Ts)); AssignOrToJSValHelper<0>(rq, argv, params...); return CallFunction_(val, name, argv, ret); } @@ -215,7 +215,7 @@ ScriptRequest rq(this); JS::RootedValue jsRet(rq.cx); JS::AutoValueVector argv(rq.cx); - argv.resize(sizeof...(Ts)); + (void)argv.resize(sizeof...(Ts)); AssignOrToJSValHelper<0>(rq, argv, params...); return CallFunction_(val, name, argv, &jsRet); } Index: ps/trunk/source/scriptinterface/ScriptContext.h =================================================================== --- ps/trunk/source/scriptinterface/ScriptContext.h +++ ps/trunk/source/scriptinterface/ScriptContext.h @@ -23,16 +23,14 @@ #include -constexpr int STACK_CHUNK_SIZE = 8192; - // Those are minimal defaults. The runtime for the main game is larger and GCs upon a larger growth. constexpr int DEFAULT_CONTEXT_SIZE = 16 * 1024 * 1024; constexpr int DEFAULT_HEAP_GROWTH_BYTES_GCTRIGGER = 2 * 1024 * 1024; /** - * Abstraction around a SpiderMonkey JSRuntime/JSContext. + * Abstraction around a SpiderMonkey JSContext. * - * A single ScriptContext, with the associated runtime and context, + * A single ScriptContext, with the associated context, * should only be used on a single thread. * * (One means to share data between threads and contexts is to create @@ -76,12 +74,10 @@ void RegisterCompartment(JSCompartment* cmpt); void UnRegisterCompartment(JSCompartment* cmpt); - JSRuntime* GetJSRuntime() const { return m_rt; } - /** * GetGeneralJSContext returns the context without starting a GC request and without * entering any compartment. It should only be used in specific situations, such as - * creating a new compartment, or as an unsafe alternative to GetJSRuntime. + * creating a new compartment, or when initializing a persistent rooted. * If you need the compartmented context of a ScriptInterface, you should create a * ScriptRequest and use the context from that. */ @@ -89,7 +85,6 @@ private: - JSRuntime* m_rt; JSContext* m_cx; void PrepareCompartmentsForIncrementalGC() const; Index: ps/trunk/source/scriptinterface/ScriptContext.cpp =================================================================== --- ps/trunk/source/scriptinterface/ScriptContext.cpp +++ ps/trunk/source/scriptinterface/ScriptContext.cpp @@ -25,7 +25,7 @@ #include "scriptinterface/ScriptInterface.h" -void GCSliceCallbackHook(JSRuntime* UNUSED(rt), JS::GCProgress progress, const JS::GCDescription& UNUSED(desc)) +void GCSliceCallbackHook(JSContext* UNUSED(cx), JS::GCProgress progress, const JS::GCDescription& UNUSED(desc)) { /* * During non-incremental GC, the GC is bracketed by JSGC_CYCLE_BEGIN/END @@ -69,7 +69,7 @@ if (progress == JS::GCProgress::GC_CYCLE_BEGIN) printf("starting cycle ===========================================\n"); - const char16_t* str = desc.formatMessage(rt); + const char16_t* str = desc.formatMessage(cx); int len = 0; for(int i = 0; i < 10000; i++) @@ -103,35 +103,32 @@ { ENSURE(ScriptEngine::IsInitialised() && "The ScriptEngine must be initialized before constructing any ScriptContexts!"); - m_rt = JS_NewRuntime(contextSize, JS::DefaultNurseryBytes, nullptr); - ENSURE(m_rt); // TODO: error handling + m_cx = JS_NewContext(contextSize, JS::DefaultNurseryBytes, nullptr); + ENSURE(m_cx); // TODO: error handling + + ENSURE(JS::InitSelfHostedCode(m_cx)); - JS::SetGCSliceCallback(m_rt, GCSliceCallbackHook); + JS::SetGCSliceCallback(m_cx, GCSliceCallbackHook); - JS_SetGCParameter(m_rt, JSGC_MAX_MALLOC_BYTES, m_ContextSize); - JS_SetGCParameter(m_rt, JSGC_MAX_BYTES, m_ContextSize); - JS_SetGCParameter(m_rt, JSGC_MODE, JSGC_MODE_INCREMENTAL); + JS_SetGCParameter(m_cx, JSGC_MAX_MALLOC_BYTES, m_ContextSize); + JS_SetGCParameter(m_cx, JSGC_MAX_BYTES, m_ContextSize); + JS_SetGCParameter(m_cx, JSGC_MODE, JSGC_MODE_INCREMENTAL); // The whole heap-growth mechanism seems to work only for non-incremental GCs. // We disable it to make it more clear if full GCs happen triggered by this JSAPI internal mechanism. - JS_SetGCParameter(m_rt, JSGC_DYNAMIC_HEAP_GROWTH, false); - - JS_SetErrorReporter(m_rt, ScriptException::ErrorReporter); + JS_SetGCParameter(m_cx, JSGC_DYNAMIC_HEAP_GROWTH, false); - m_cx = JS_NewContext(m_rt, STACK_CHUNK_SIZE); - ENSURE(m_cx); // TODO: error handling - - JS_SetOffthreadIonCompilationEnabled(m_rt, true); + JS_SetOffthreadIonCompilationEnabled(m_cx, true); // For GC debugging: // JS_SetGCZeal(m_cx, 2, JS_DEFAULT_ZEAL_FREQ); JS_SetContextPrivate(m_cx, nullptr); - JS_SetGlobalJitCompilerOption(m_rt, JSJITCOMPILER_ION_ENABLE, 1); - JS_SetGlobalJitCompilerOption(m_rt, JSJITCOMPILER_BASELINE_ENABLE, 1); + JS_SetGlobalJitCompilerOption(m_cx, JSJITCOMPILER_ION_ENABLE, 1); + JS_SetGlobalJitCompilerOption(m_cx, JSJITCOMPILER_BASELINE_ENABLE, 1); - JS::RuntimeOptionsRef(m_cx) + JS::ContextOptionsRef(m_cx) .setExtraWarnings(true) .setWerror(false) .setStrictMode(true); @@ -144,8 +141,6 @@ ENSURE(ScriptEngine::IsInitialised() && "The ScriptEngine must be active (initialized and not yet shut down) when destroying a ScriptContext!"); JS_DestroyContext(m_cx); - JS_DestroyRuntime(m_rt); - ScriptEngine::GetSingleton().UnRegisterContext(m_cx); } @@ -165,7 +160,7 @@ { PROFILE2("MaybeIncrementalGC"); - if (JS::IsIncrementalGCEnabled(m_rt)) + if (JS::IsIncrementalGCEnabled(m_cx)) { // The idea is to get the heap size after a completed GC and trigger the next GC when the heap size has // reached m_LastGCBytes + X. @@ -184,7 +179,7 @@ m_LastGCCheck = timer_Time(); - int gcBytes = JS_GetGCParameter(m_rt, JSGC_BYTES); + int gcBytes = JS_GetGCParameter(m_cx, JSGC_BYTES); #if GC_DEBUG_PRINT std::cout << "gcBytes: " << gcBytes / 1024 << " KB" << std::endl; @@ -201,10 +196,10 @@ // Run an additional incremental GC slice if the currently running incremental GC isn't over yet // ... or // start a new incremental GC if the JS heap size has grown enough for a GC to make sense - if (JS::IsIncrementalGCInProgress(m_rt) || (gcBytes - m_LastGCBytes > m_HeapGrowthBytesGCTrigger)) + if (JS::IsIncrementalGCInProgress(m_cx) || (gcBytes - m_LastGCBytes > m_HeapGrowthBytesGCTrigger)) { #if GC_DEBUG_PRINT - if (JS::IsIncrementalGCInProgress(m_rt)) + if (JS::IsIncrementalGCInProgress(m_cx)) printf("An incremental GC cycle is in progress. \n"); else printf("GC needed because JSGC_BYTES - m_LastGCBytes > m_HeapGrowthBytesGCTrigger \n" @@ -218,13 +213,13 @@ // fast enough. if (gcBytes > m_ContextSize / 2) { - if (JS::IsIncrementalGCInProgress(m_rt)) + if (JS::IsIncrementalGCInProgress(m_cx)) { #if GC_DEBUG_PRINT printf("Finishing incremental GC because gcBytes > m_ContextSize / 2. \n"); #endif PrepareCompartmentsForIncrementalGC(); - JS::FinishIncrementalGC(m_rt, JS::gcreason::REFRESH_FRAME); + JS::FinishIncrementalGC(m_cx, JS::gcreason::REFRESH_FRAME); } else { @@ -240,23 +235,23 @@ #if GC_DEBUG_PRINT printf("Running full GC because gcBytes > m_ContextSize / 2. \n"); #endif - JS_GC(m_rt); + JS_GC(m_cx); } } } else { #if GC_DEBUG_PRINT - if (!JS::IsIncrementalGCInProgress(m_rt)) + if (!JS::IsIncrementalGCInProgress(m_cx)) printf("Starting incremental GC \n"); else printf("Running incremental GC slice \n"); #endif PrepareCompartmentsForIncrementalGC(); - if (!JS::IsIncrementalGCInProgress(m_rt)) - JS::StartIncrementalGC(m_rt, GC_NORMAL, JS::gcreason::REFRESH_FRAME, GCSliceTimeBudget); + if (!JS::IsIncrementalGCInProgress(m_cx)) + JS::StartIncrementalGC(m_cx, GC_NORMAL, JS::gcreason::REFRESH_FRAME, GCSliceTimeBudget); else - JS::IncrementalGCSlice(m_rt, JS::gcreason::REFRESH_FRAME, GCSliceTimeBudget); + JS::IncrementalGCSlice(m_cx, JS::gcreason::REFRESH_FRAME, GCSliceTimeBudget); } m_LastGCBytes = gcBytes; } @@ -265,10 +260,10 @@ void ScriptContext::ShrinkingGC() { - JS_SetGCParameter(m_rt, JSGC_MODE, JSGC_MODE_COMPARTMENT); - JS::PrepareForFullGC(m_rt); - JS::GCForReason(m_rt, GC_SHRINK, JS::gcreason::REFRESH_FRAME); - JS_SetGCParameter(m_rt, JSGC_MODE, JSGC_MODE_INCREMENTAL); + JS_SetGCParameter(m_cx, JSGC_MODE, JSGC_MODE_ZONE); + JS::PrepareForFullGC(m_cx); + JS::GCForReason(m_cx, GC_SHRINK, JS::gcreason::REFRESH_FRAME); + JS_SetGCParameter(m_cx, JSGC_MODE, JSGC_MODE_INCREMENTAL); } void ScriptContext::PrepareCompartmentsForIncrementalGC() const Index: ps/trunk/source/scriptinterface/ScriptConversions.cpp =================================================================== --- ps/trunk/source/scriptinterface/ScriptConversions.cpp +++ ps/trunk/source/scriptinterface/ScriptConversions.cpp @@ -28,28 +28,7 @@ #define FAIL(msg) STMT(LOGERROR(msg); return false) // Implicit type conversions often hide bugs, so warn about them -#define WARN_IF_NOT(c, v) STMT(if (!(c)) { JS_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 -static const char* InformalValueTypeName(const JS::Value& v) -{ - if (v.isObject()) - return "object"; - if (v.isString()) - return "string"; - if (v.isSymbol()) - return "symbol"; - if (v.isNumber()) - return "number"; - if (v.isBoolean()) - return "boolean"; - if (v.isNull()) - return "null"; - if (v.isUndefined()) - return "undefined"; - return "value"; -} +#define WARN_IF_NOT(c, v) STMT(if (!(c)) { JS_ReportWarningUTF8(rq.cx, "Script value conversion check failed: %s (got type %s)", #c, JS::InformalValueTypeName(v)); }) template<> bool ScriptInterface::FromJSVal(const ScriptRequest& rq, JS::HandleValue v, bool& out) { Index: ps/trunk/source/scriptinterface/ScriptExceptions.h =================================================================== --- ps/trunk/source/scriptinterface/ScriptExceptions.h +++ ps/trunk/source/scriptinterface/ScriptExceptions.h @@ -39,8 +39,6 @@ */ bool CatchPending(const ScriptRequest& rq); -void ErrorReporter(JSContext* rt, const char* message, JSErrorReport* report); - /** * Raise a JS exception from C++ code. * This is only really relevant in JSNative functions that don't use ObjectOpResult, Index: ps/trunk/source/scriptinterface/ScriptExceptions.cpp =================================================================== --- ps/trunk/source/scriptinterface/ScriptExceptions.cpp +++ ps/trunk/source/scriptinterface/ScriptExceptions.cpp @@ -73,10 +73,9 @@ msg << " line " << report->lineno << "\n"; } - // TODO SM52: - // msg << report->message(); + msg << report->message().c_str(); - JS::RootedObject stackObj(rq.cx, ExceptionStackOrNull(rq.cx, excnObj)); + JS::RootedObject stackObj(rq.cx, ExceptionStackOrNull(excnObj)); JS::RootedValue stackVal(rq.cx, JS::ObjectOrNullValue(stackObj)); if (!stackVal.isNull()) { @@ -97,55 +96,10 @@ return true; } -void ScriptException::ErrorReporter(JSContext* cx, const char* message, JSErrorReport* report) -{ - if (!ScriptInterface::GetScriptInterfaceAndCBData(cx)) - { - LOGERROR("Javascript - Out of Memory error"); - return; - } - ScriptRequest rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); - - std::stringstream msg; - bool isWarning = JSREPORT_IS_WARNING(report->flags); - msg << (isWarning ? "JavaScript warning: " : "JavaScript error: "); - if (report->filename) - { - msg << report->filename; - msg << " line " << report->lineno << "\n"; - } - - msg << message; - - // If there is an exception, then print its stack trace - JS::RootedValue excn(rq.cx); - if (JS_GetPendingException(rq.cx, &excn) && excn.isObject()) - { - JS::RootedValue stackVal(rq.cx); - JS::RootedObject excnObj(rq.cx, &excn.toObject()); - JS_GetProperty(rq.cx, excnObj, "stack", &stackVal); - - std::string stackText; - ScriptInterface::FromJSVal(rq, stackVal, stackText); - - std::istringstream stream(stackText); - for (std::string line; std::getline(stream, line);) - msg << "\n " << line; - } - - if (isWarning) - LOGWARNING("%s", msg.str().c_str()); - else - LOGERROR("%s", msg.str().c_str()); - - // When running under Valgrind, print more information in the error message - // VALGRIND_PRINTF_BACKTRACE("->"); -} - void ScriptException::Raise(const ScriptRequest& rq, const char* format, ...) { va_list ap; va_start(ap, format); - JS_ReportError(rq.cx, format, ap); + JS_ReportErrorUTF8(rq.cx, format, ap); va_end(ap); } Index: ps/trunk/source/scriptinterface/ScriptInterface.h =================================================================== --- ps/trunk/source/scriptinterface/ScriptInterface.h +++ ps/trunk/source/scriptinterface/ScriptInterface.h @@ -119,7 +119,14 @@ void SetCallbackData(void* pCBData); static CmptPrivate* GetScriptInterfaceAndCBData(JSContext* cx); - JSRuntime* GetJSRuntime() const; + /** + * GetGeneralJSContext returns the context without starting a GC request and without + * entering the ScriptInterface compartment. It should only be used in specific situations, + * for instance when initializing a persistent rooted. + * If you need the compartmented context of the ScriptInterface, you should create a + * ScriptInterface::Request and use the context from that. + */ + JSContext* GetGeneralJSContext() const; shared_ptr GetContext() const; /** @@ -290,14 +297,6 @@ bool LoadGlobalScriptFile(const VfsPath& path) const; /** - * Construct a new value (usable in this ScriptInterface's compartment) by cloning - * a value from a different compartment. - * Complex values (functions, XML, etc) won't be cloned correctly, but basic - * types and cyclic references should be fine. - */ - JS::Value CloneValueFromOtherCompartment(const ScriptInterface& otherCompartment, JS::HandleValue val) const; - - /** * Convert a JS::Value to a C++ type. (This might trigger GC.) */ template static bool FromJSVal(const ScriptRequest& rq, const JS::HandleValue val, T& ret); @@ -331,18 +330,18 @@ * We wrap them in shared_ptr so memory management is automatic and * thread-safe. */ - class StructuredClone - { - NONCOPYABLE(StructuredClone); - public: - StructuredClone(); - ~StructuredClone(); - u64* m_Data; - size_t m_Size; - }; + using StructuredClone = shared_ptr; + + StructuredClone WriteStructuredClone(JS::HandleValue v, bool sameThread) const; + void ReadStructuredClone(const StructuredClone& ptr, JS::MutableHandleValue ret) const; - shared_ptr WriteStructuredClone(JS::HandleValue v) const; - void ReadStructuredClone(const shared_ptr& ptr, JS::MutableHandleValue ret) const; + /** + * Construct a new value (usable in this ScriptInterface's compartment) by cloning + * a value from a different compartment. + * Complex values (functions, XML, etc) won't be cloned correctly, but basic + * types and cyclic references should be fine. + */ + JS::Value CloneValueFromOtherCompartment(const ScriptInterface& otherCompartment, JS::HandleValue val, bool sameThread) const; /** * Retrieve the private data field of a JSObject that is an instance of the given JSClass. Index: ps/trunk/source/scriptinterface/ScriptInterface.cpp =================================================================== --- ps/trunk/source/scriptinterface/ScriptInterface.cpp +++ ps/trunk/source/scriptinterface/ScriptInterface.cpp @@ -94,8 +94,7 @@ namespace { -JSClass global_class = { - "global", JSCLASS_GLOBAL_FLAGS, +JSClassOps global_classops = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, @@ -103,6 +102,10 @@ JS_GlobalObjectTraceHook }; +JSClass global_class = { + "global", JSCLASS_GLOBAL_FLAGS, &global_classops +}; + // Functions in the global namespace: bool print(JSContext* cx, uint argc, JS::Value* vp) @@ -319,12 +322,15 @@ } ScriptInterface_impl::ScriptInterface_impl(const char* nativeScopeName, const shared_ptr& context) : - m_context(context), m_cx(context->GetGeneralJSContext()), m_glob(context->GetJSRuntime()), m_nativeScope(context->GetJSRuntime()) + m_context(context), m_cx(context->GetGeneralJSContext()), m_glob(context->GetGeneralJSContext()), m_nativeScope(context->GetGeneralJSContext()) { - JS::CompartmentOptions opt; - opt.setVersion(JSVERSION_LATEST); + JS::CompartmentCreationOptions creationOpt; // Keep JIT code during non-shrinking GCs. This brings a quite big performance improvement. - opt.setPreserveJitCode(true); + creationOpt.setPreserveJitCode(true); + JS::CompartmentBehaviors behaviors; + behaviors.setVersion(JSVERSION_LATEST); + + JS::CompartmentOptions opt(creationOpt, behaviors); JSAutoRequest rq(m_cx); m_glob = JS_NewGlobalObject(m_cx, &global_class, nullptr, JS::OnNewGlobalHookOption::FireOnNewGlobalHook, opt); @@ -446,9 +452,9 @@ m->Register(name, fptr, (uint)nargs); } -JSRuntime* ScriptInterface::GetJSRuntime() const +JSContext* ScriptInterface::GetGeneralJSContext() const { - return m->m_context->GetJSRuntime(); + return m->m_context->GetGeneralJSContext(); } shared_ptr ScriptInterface::GetContext() const @@ -560,7 +566,7 @@ return false; if (found) { - JS::Rooted desc(rq.cx); + JS::Rooted desc(rq.cx); if (!JS_GetOwnPropertyDescriptor(rq.cx, global, name, &desc)) return false; @@ -971,15 +977,7 @@ Stringifier str; 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(GetJSRuntime(), nullptr); - - bool ok = JS_Stringify(rq.cx, obj, nullptr, indentVal, &Stringifier::callback, &str); - - // Restore error reporter - JS_SetErrorReporter(GetJSRuntime(), er); - - if (ok) + if (JS_Stringify(rq.cx, obj, nullptr, indentVal, &Stringifier::callback, &str)) return str.stream.str(); // Drop exceptions raised by cyclic values before trying something else @@ -994,48 +992,37 @@ return utf8_from_wstring(source); } -JS::Value ScriptInterface::CloneValueFromOtherCompartment(const ScriptInterface& otherCompartment, JS::HandleValue val) const +JS::Value ScriptInterface::CloneValueFromOtherCompartment(const ScriptInterface& otherCompartment, JS::HandleValue val, bool sameThread) const { PROFILE("CloneValueFromOtherCompartment"); ScriptRequest rq(this); JS::RootedValue out(rq.cx); - shared_ptr structuredClone = otherCompartment.WriteStructuredClone(val); + ScriptInterface::StructuredClone structuredClone = otherCompartment.WriteStructuredClone(val, sameThread); ReadStructuredClone(structuredClone, &out); return out.get(); } -ScriptInterface::StructuredClone::StructuredClone() : - m_Data(NULL), m_Size(0) +ScriptInterface::StructuredClone ScriptInterface::WriteStructuredClone(JS::HandleValue v, bool sameThread) const { -} + ScriptRequest rq(this); -ScriptInterface::StructuredClone::~StructuredClone() -{ - if (m_Data) - JS_ClearStructuredClone(m_Data, m_Size, NULL, NULL); -} + JS::StructuredCloneScope scope = sameThread ? JS::StructuredCloneScope::SameProcessSameThread : JS::StructuredCloneScope::SameProcessDifferentThread; + ScriptInterface::StructuredClone ret(new JSStructuredCloneData(scope)); + JS::CloneDataPolicy policy; -shared_ptr ScriptInterface::WriteStructuredClone(JS::HandleValue v) const -{ - ScriptRequest rq(this); - u64* data = NULL; - size_t nbytes = 0; - if (!JS_WriteStructuredClone(rq.cx, v, &data, &nbytes, NULL, NULL, JS::UndefinedHandleValue)) + if (!JS_WriteStructuredClone(rq.cx, v, ret.get(), scope, policy, nullptr, nullptr, JS::UndefinedHandleValue)) { debug_warn(L"Writing a structured clone with JS_WriteStructuredClone failed!"); ScriptException::CatchPending(rq); - return shared_ptr(); + return ScriptInterface::StructuredClone(); } - shared_ptr ret(new StructuredClone); - ret->m_Data = data; - ret->m_Size = nbytes; return ret; } -void ScriptInterface::ReadStructuredClone(const shared_ptr& ptr, JS::MutableHandleValue ret) const +void ScriptInterface::ReadStructuredClone(const ScriptInterface::StructuredClone& ptr, JS::MutableHandleValue ret) const { ScriptRequest rq(this); - if (!JS_ReadStructuredClone(rq.cx, ptr->m_Data, ptr->m_Size, JS_STRUCTURED_CLONE_VERSION, ret, NULL, NULL)) + if (!JS_ReadStructuredClone(rq.cx, *ptr, JS_STRUCTURED_CLONE_VERSION, ptr->scope(), ret, nullptr, nullptr)) ScriptException::CatchPending(rq); } Index: ps/trunk/source/scriptinterface/ScriptStats.cpp =================================================================== --- ps/trunk/source/scriptinterface/ScriptStats.cpp +++ ps/trunk/source/scriptinterface/ScriptStats.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2015 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 @@ -84,28 +84,28 @@ { if (col == 0) return "max nominal heap bytes"; - uint32_t n = JS_GetGCParameter(m_ScriptInterfaces.at(col-1).first->GetJSRuntime(), JSGC_MAX_BYTES); + uint32_t n = JS_GetGCParameter(m_ScriptInterfaces.at(col-1).first->GetGeneralJSContext(), JSGC_MAX_BYTES); return CStr::FromUInt(n); } case Row_MaxMallocBytes: { if (col == 0) return "max JS_malloc bytes"; - uint32_t n = JS_GetGCParameter(m_ScriptInterfaces.at(col-1).first->GetJSRuntime(), JSGC_MAX_MALLOC_BYTES); + uint32_t n = JS_GetGCParameter(m_ScriptInterfaces.at(col-1).first->GetGeneralJSContext(), JSGC_MAX_MALLOC_BYTES); return CStr::FromUInt(n); } case Row_Bytes: { if (col == 0) return "allocated bytes"; - uint32_t n = JS_GetGCParameter(m_ScriptInterfaces.at(col-1).first->GetJSRuntime(), JSGC_BYTES); + uint32_t n = JS_GetGCParameter(m_ScriptInterfaces.at(col-1).first->GetGeneralJSContext(), JSGC_BYTES); return CStr::FromUInt(n); } case Row_NumberGC: { if (col == 0) return "number of GCs"; - uint32_t n = JS_GetGCParameter(m_ScriptInterfaces.at(col-1).first->GetJSRuntime(), JSGC_NUMBER); + uint32_t n = JS_GetGCParameter(m_ScriptInterfaces.at(col-1).first->GetGeneralJSContext(), JSGC_NUMBER); return CStr::FromUInt(n); } default: Index: ps/trunk/source/scriptinterface/ScriptTypes.h =================================================================== --- ps/trunk/source/scriptinterface/ScriptTypes.h +++ ps/trunk/source/scriptinterface/ScriptTypes.h @@ -55,6 +55,8 @@ #if MSC_VERSION // reduce the warning level for the SpiderMonkey headers # pragma warning(push, 1) +// ignore C4291 in +# pragma warning(disable: 4291) #endif #include "jspubtd.h" 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 @@ -40,7 +40,7 @@ ScriptInterface script("Test", "Test", g_ScriptContext); TestLogger logger; TS_ASSERT(!script.LoadScript(L"test.js", "1+")); - TS_ASSERT_STR_CONTAINS(logger.GetOutput(), "ERROR: JavaScript error: test.js line 1\nSyntaxError: expected expression, got end of script"); + TS_ASSERT_STR_CONTAINS(logger.GetOutput(), "ERROR: JavaScript error: test.js line 1\nexpected expression, got end of script"); } void test_loadscript_strict_warning() @@ -57,7 +57,7 @@ ScriptInterface script("Test", "Test", g_ScriptContext); TestLogger logger; TS_ASSERT(!script.LoadScript(L"test.js", "with(1){}")); - TS_ASSERT_STR_CONTAINS(logger.GetOutput(), "ERROR: JavaScript error: test.js line 1\nSyntaxError: strict mode code may not contain \'with\' statements"); + TS_ASSERT_STR_CONTAINS(logger.GetOutput(), "ERROR: JavaScript error: test.js line 1\nstrict mode code may not contain \'with\' statements"); } void test_clone_basic() @@ -72,7 +72,7 @@ { ScriptRequest rq2(script2); - JS::RootedValue obj2(rq2.cx, script2.CloneValueFromOtherCompartment(script1, obj1)); + JS::RootedValue obj2(rq2.cx, script2.CloneValueFromOtherCompartment(script1, obj1, true)); std::string source; TS_ASSERT(script2.CallFunction(obj2, "toSource", source)); @@ -94,7 +94,7 @@ { ScriptRequest rq2(script2); - JS::RootedValue obj2(rq2.cx, script2.CloneValueFromOtherCompartment(script1, obj1)); + JS::RootedValue obj2(rq2.cx, script2.CloneValueFromOtherCompartment(script1, obj1, true)); std::string source; TS_ASSERT(script2.CallFunction(obj2, "toSource", source)); @@ -114,7 +114,7 @@ { ScriptRequest rq2(script2); - JS::RootedValue obj2(rq2.cx, script2.CloneValueFromOtherCompartment(script1, obj1)); + JS::RootedValue obj2(rq2.cx, script2.CloneValueFromOtherCompartment(script1, obj1, true)); // Use JSAPI function to check if the values of the properties "a", "b" are equals a.x[0] JS::RootedValue prop_a(rq2.cx); @@ -125,8 +125,8 @@ TS_ASSERT(prop_a.isObject()); TS_ASSERT(prop_b.isObject()); TS_ASSERT(script2.GetProperty(prop_a, "0", &prop_x1)); - TS_ASSERT_EQUALS(prop_x1.get(), prop_a.get()); - TS_ASSERT_EQUALS(prop_x1.get(), prop_b.get()); + TS_ASSERT(prop_x1.get() == prop_a.get()); + TS_ASSERT(prop_x1.get() == prop_b.get()); } } Index: ps/trunk/source/simulation2/Simulation2.h =================================================================== --- ps/trunk/source/simulation2/Simulation2.h +++ ps/trunk/source/simulation2/Simulation2.h @@ -48,7 +48,7 @@ public: // TODO: CUnitManager should probably be handled automatically by this // module, but for now we'll have it passed in externally instead - CSimulation2(CUnitManager* unitManager, shared_ptr rt, CTerrain* terrain); + CSimulation2(CUnitManager* unitManager, shared_ptr cx, CTerrain* terrain); ~CSimulation2(); void EnableSerializationTest(); Index: ps/trunk/source/simulation2/Simulation2.cpp =================================================================== --- ps/trunk/source/simulation2/Simulation2.cpp +++ ps/trunk/source/simulation2/Simulation2.cpp @@ -49,11 +49,11 @@ class CSimulation2Impl { public: - CSimulation2Impl(CUnitManager* unitManager, shared_ptr rt, CTerrain* terrain) : - m_SimContext(), m_ComponentManager(m_SimContext, rt), + CSimulation2Impl(CUnitManager* unitManager, shared_ptr cx, CTerrain* terrain) : + m_SimContext(), m_ComponentManager(m_SimContext, cx), m_EnableOOSLog(false), m_EnableSerializationTest(false), m_RejoinTestTurn(-1), m_TestingRejoin(false), m_SecondaryTerrain(nullptr), m_SecondaryContext(nullptr), m_SecondaryComponentManager(nullptr), m_SecondaryLoadedScripts(nullptr), - m_MapSettings(rt->GetJSRuntime()), m_InitAttributes(rt->GetJSRuntime()) + m_MapSettings(cx->GetGeneralJSContext()), m_InitAttributes(cx->GetGeneralJSContext()) { m_SimContext.m_UnitManager = unitManager; m_SimContext.m_Terrain = terrain; @@ -164,7 +164,7 @@ void InitRNGSeedSimulation(); void InitRNGSeedAI(); - static std::vector CloneCommandsFromOtherContext(const ScriptInterface& oldScript, const ScriptInterface& newScript, + static std::vector CloneCommandsFromOtherCompartment(const ScriptInterface& oldScript, const ScriptInterface& newScript, const std::vector& commands) { std::vector newCommands; @@ -173,7 +173,7 @@ ScriptRequest rqNew(newScript); for (const SimulationCommand& command : commands) { - JS::RootedValue tmpCommand(rqNew.cx, newScript.CloneValueFromOtherCompartment(oldScript, command.data)); + JS::RootedValue tmpCommand(rqNew.cx, newScript.CloneValueFromOtherCompartment(oldScript, command.data, true)); newScript.FreezeObject(tmpCommand, true); SimulationCommand cmd(command.player, rqNew.cx, tmpCommand); newCommands.emplace_back(std::move(cmd)); @@ -424,7 +424,7 @@ ScriptRequest rq2(m_SecondaryComponentManager->GetScriptInterface()); JS::RootedValue mapSettingsCloned(rq2.cx, m_SecondaryComponentManager->GetScriptInterface().CloneValueFromOtherCompartment( - scriptInterface, m_MapSettings)); + scriptInterface, m_MapSettings, true)); ENSURE(LoadTriggerScripts(*m_SecondaryComponentManager, mapSettingsCloned, m_SecondaryLoadedScripts)); } @@ -446,7 +446,7 @@ scriptInterface.GetProperty(m_InitAttributes, "map", mapFile); VfsPath mapfilename = VfsPath(mapFile).ChangeExtension(L".pmp"); - mapReader->LoadMap(mapfilename, scriptInterface.GetJSRuntime(), JS::UndefinedHandleValue, + mapReader->LoadMap(mapfilename, *scriptInterface.GetContext(), JS::UndefinedHandleValue, m_SecondaryTerrain, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, m_SecondaryContext, INVALID_PLAYER, true); // throws exception on failure } @@ -477,7 +477,7 @@ ENSURE(m_ComponentManager.ComputeStateHash(primaryStateAfter.hash, false)); UpdateComponents(*m_SecondaryContext, turnLengthFixed, - CloneCommandsFromOtherContext(scriptInterface, m_SecondaryComponentManager->GetScriptInterface(), commands)); + CloneCommandsFromOtherCompartment(scriptInterface, m_SecondaryComponentManager->GetScriptInterface(), commands)); SerializationTestState secondaryStateAfter; ENSURE(m_SecondaryComponentManager->SerializeState(secondaryStateAfter.state)); if (serializationTestHash) @@ -638,8 +638,8 @@ //////////////////////////////////////////////////////////////// -CSimulation2::CSimulation2(CUnitManager* unitManager, shared_ptr rt, CTerrain* terrain) : - m(new CSimulation2Impl(unitManager, rt, terrain)) +CSimulation2::CSimulation2(CUnitManager* unitManager, shared_ptr cx, CTerrain* terrain) : + m(new CSimulation2Impl(unitManager, cx, terrain)) { } Index: ps/trunk/source/simulation2/components/CCmpAIManager.cpp =================================================================== --- ps/trunk/source/simulation2/components/CCmpAIManager.cpp +++ ps/trunk/source/simulation2/components/CCmpAIManager.cpp @@ -86,7 +86,7 @@ CAIPlayer(CAIWorker& worker, const std::wstring& aiName, player_id_t player, u8 difficulty, const std::wstring& behavior, shared_ptr scriptInterface) : m_Worker(worker), m_AIName(aiName), m_Player(player), m_Difficulty(difficulty), m_Behavior(behavior), - m_ScriptInterface(scriptInterface), m_Obj(scriptInterface->GetJSRuntime()) + m_ScriptInterface(scriptInterface), m_Obj(scriptInterface->GetGeneralJSContext()) { } @@ -200,14 +200,14 @@ shared_ptr m_ScriptInterface; JS::PersistentRootedValue m_Obj; - std::vector > m_Commands; + std::vector m_Commands; }; public: struct SCommandSets { player_id_t player; - std::vector > commands; + std::vector commands; }; CAIWorker() : @@ -216,17 +216,17 @@ m_CommandsComputed(true), m_HasLoadedEntityTemplates(false), m_HasSharedComponent(false), - m_EntityTemplates(g_ScriptContext->GetJSRuntime()), - m_SharedAIObj(g_ScriptContext->GetJSRuntime()), - m_PassabilityMapVal(g_ScriptContext->GetJSRuntime()), - m_TerritoryMapVal(g_ScriptContext->GetJSRuntime()) + m_EntityTemplates(g_ScriptContext->GetGeneralJSContext()), + m_SharedAIObj(g_ScriptContext->GetGeneralJSContext()), + m_PassabilityMapVal(g_ScriptContext->GetGeneralJSContext()), + m_TerritoryMapVal(g_ScriptContext->GetGeneralJSContext()) { m_ScriptInterface->ReplaceNondeterministicRNG(m_RNG); m_ScriptInterface->SetCallbackData(static_cast (this)); - JS_AddExtraGCRootsTracer(m_ScriptInterface->GetJSRuntime(), Trace, this); + JS_AddExtraGCRootsTracer(m_ScriptInterface->GetGeneralJSContext(), Trace, this); m_ScriptInterface->RegisterFunction("PostCommand"); m_ScriptInterface->RegisterFunction("IncludeModule"); @@ -245,7 +245,7 @@ ~CAIWorker() { - JS_RemoveExtraGCRootsTracer(m_ScriptInterface->GetJSRuntime(), Trace, this); + JS_RemoveExtraGCRootsTracer(m_ScriptInterface->GetGeneralJSContext(), Trace, this); } bool HasLoadedEntityTemplates() const { return m_HasLoadedEntityTemplates; } @@ -299,7 +299,7 @@ { if (m_Players[i]->m_Player == playerid) { - m_Players[i]->m_Commands.push_back(m_ScriptInterface->WriteStructuredClone(cmd)); + m_Players[i]->m_Commands.push_back(m_ScriptInterface->WriteStructuredClone(cmd, false)); return; } } @@ -485,7 +485,7 @@ return true; } - bool RunGamestateInit(const shared_ptr& gameState, const Grid& passabilityMap, const Grid& territoryMap, + bool RunGamestateInit(const ScriptInterface::StructuredClone& gameState, const Grid& passabilityMap, const Grid& territoryMap, const std::map& nonPathfindingPassClassMasks, const std::map& pathfindingPassClassMasks) { // this will be run last by InitGame.js, passing the full game representation. @@ -521,7 +521,7 @@ return true; } - void UpdateGameState(const shared_ptr& gameState) + void UpdateGameState(const ScriptInterface::StructuredClone& gameState) { ENSURE(m_CommandsComputed); m_GameState = gameState; @@ -765,7 +765,7 @@ { JS::RootedValue val(rq.cx); deserializer.ScriptVal("command", &val); - m_Players.back()->m_Commands.push_back(m_ScriptInterface->WriteStructuredClone(val)); + m_Players.back()->m_Commands.push_back(m_ScriptInterface->WriteStructuredClone(val, false)); } bool hasCustomDeserialize = m_ScriptInterface->HasProperty(m_Players.back()->m_Obj, "Deserialize"); @@ -888,7 +888,7 @@ std::set m_LoadedModules; - shared_ptr m_GameState; + ScriptInterface::StructuredClone m_GameState; Grid m_PassabilityMap; JS::PersistentRootedValue m_PassabilityMapVal; Grid m_TerritoryMap; @@ -1016,7 +1016,8 @@ if (cmpPathfinder) cmpPathfinder->GetPassabilityClasses(nonPathfindingPassClassMasks, pathfindingPassClassMasks); - m_Worker.RunGamestateInit(scriptInterface.WriteStructuredClone(state), *passabilityMap, *territoryMap, nonPathfindingPassClassMasks, pathfindingPassClassMasks); + m_Worker.RunGamestateInit(scriptInterface.WriteStructuredClone(state, false), + *passabilityMap, *territoryMap, nonPathfindingPassClassMasks, pathfindingPassClassMasks); } virtual void StartComputation() @@ -1041,7 +1042,7 @@ LoadPathfinderClasses(state); // add the pathfinding classes to it // Update the game state - m_Worker.UpdateGameState(scriptInterface.WriteStructuredClone(state)); + m_Worker.UpdateGameState(scriptInterface.WriteStructuredClone(state, false)); // Update the pathfinding data CmpPtr cmpPathfinder(GetSystemEntity()); Index: ps/trunk/source/simulation2/components/ICmpAIManager.cpp =================================================================== --- ps/trunk/source/simulation2/components/ICmpAIManager.cpp +++ ps/trunk/source/simulation2/components/ICmpAIManager.cpp @@ -39,7 +39,7 @@ public: GetAIsHelper(const ScriptInterface& scriptInterface) : m_ScriptInterface(scriptInterface), - m_AIs(scriptInterface.GetJSRuntime()) + m_AIs(scriptInterface.GetGeneralJSContext()) { ScriptRequest rq(m_ScriptInterface); m_AIs = JS_NewArrayObject(rq.cx, 0); Index: ps/trunk/source/simulation2/components/tests/test_Pathfinder.h =================================================================== --- ps/trunk/source/simulation2/components/tests/test_Pathfinder.h +++ ps/trunk/source/simulation2/components/tests/test_Pathfinder.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 @@ -28,6 +28,7 @@ #include "lib/tex/tex.h" #include "ps/Loader.h" #include "ps/Pyrogenesis.h" +#include "scriptinterface/ScriptContext.h" #include "simulation2/Simulation2.h" class TestCmpPathfinder : public CxxTest::TestSuite @@ -147,7 +148,7 @@ LDR_BeginRegistering(); mapReader->LoadMap(L"maps/skirmishes/Median Oasis (2).pmp", - sim2.GetScriptInterface().GetJSRuntime(), JS::UndefinedHandleValue, + *sim2.GetScriptInterface().GetContext(), JS::UndefinedHandleValue, &terrain, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &sim2, &sim2.GetSimContext(), -1, false); LDR_EndRegistering(); @@ -258,7 +259,7 @@ LDR_BeginRegistering(); mapReader->LoadMap(L"maps/scenarios/Peloponnese.pmp", - sim2.GetScriptInterface().GetJSRuntime(), JS::UndefinedHandleValue, + *sim2.GetScriptInterface().GetContext(), JS::UndefinedHandleValue, &terrain, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &sim2, &sim2.GetSimContext(), -1, false); LDR_EndRegistering(); @@ -315,7 +316,7 @@ LDR_BeginRegistering(); mapReader->LoadMap(L"maps/scenarios/Peloponnese.pmp", - sim2.GetScriptInterface().GetJSRuntime(), JS::UndefinedHandleValue, + *sim2.GetScriptInterface().GetContext(), JS::UndefinedHandleValue, &terrain, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &sim2, &sim2.GetSimContext(), -1, false); LDR_EndRegistering(); Index: ps/trunk/source/simulation2/scripting/EngineScriptConversions.cpp =================================================================== --- ps/trunk/source/simulation2/scripting/EngineScriptConversions.cpp +++ ps/trunk/source/simulation2/scripting/EngineScriptConversions.cpp @@ -168,8 +168,11 @@ args[1].setNumber(val.Y.ToDouble()); args[2].setNumber(val.Z.ToDouble()); - if (!JS::Construct(rq.cx, valueVector3D, args, ret)) + JS::RootedObject objVec(rq.cx); + if (!JS::Construct(rq.cx, valueVector3D, args, &objVec)) FAIL_VOID("Failed to construct Vector3D object"); + + ret.setObject(*objVec); } template<> bool ScriptInterface::FromJSVal(const ScriptRequest& rq, JS::HandleValue v, CFixedVector2D& out) @@ -200,8 +203,11 @@ args[0].setNumber(val.X.ToDouble()); args[1].setNumber(val.Y.ToDouble()); - if (!JS::Construct(rq.cx, valueVector2D, args, ret)) + JS::RootedObject objVec(rq.cx); + if (!JS::Construct(rq.cx, valueVector2D, args, &objVec)) FAIL_VOID("Failed to construct Vector2D object"); + + ret.setObject(*objVec); } template<> void ScriptInterface::ToJSVal >(const ScriptRequest& rq, JS::MutableHandleValue ret, const Grid& val) 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 @@ -51,11 +51,11 @@ return JS::UndefinedValue(); ScriptRequest rqSim(sim->GetScriptInterface()); - JS::RootedValue arg(rqSim.cx, sim->GetScriptInterface().CloneValueFromOtherCompartment(*(pCmptPrivate->pScriptInterface), data)); + JS::RootedValue arg(rqSim.cx, sim->GetScriptInterface().CloneValueFromOtherCompartment(*(pCmptPrivate->pScriptInterface), data, false)); JS::RootedValue ret(rqSim.cx); cmpGuiInterface->ScriptCall(g_Game->GetViewedPlayerID(), name, arg, &ret); - return pCmptPrivate->pScriptInterface->CloneValueFromOtherCompartment(sim->GetScriptInterface(), ret); + return pCmptPrivate->pScriptInterface->CloneValueFromOtherCompartment(sim->GetScriptInterface(), ret, false); } void JSI_Simulation::PostNetworkCommand(ScriptInterface::CmptPrivate* pCmptPrivate, JS::HandleValue cmd) @@ -71,7 +71,8 @@ return; ScriptRequest rqSim(sim->GetScriptInterface()); - JS::RootedValue cmd2(rqSim.cx, sim->GetScriptInterface().CloneValueFromOtherCompartment(*(pCmptPrivate->pScriptInterface), cmd)); + JS::RootedValue cmd2(rqSim.cx, + sim->GetScriptInterface().CloneValueFromOtherCompartment(*(pCmptPrivate->pScriptInterface), cmd, false)); cmpCommandQueue->PostNetworkCommand(cmd2); } Index: ps/trunk/source/simulation2/scripting/MessageTypeConversions.cpp =================================================================== --- ps/trunk/source/simulation2/scripting/MessageTypeConversions.cpp +++ ps/trunk/source/simulation2/scripting/MessageTypeConversions.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 @@ -53,7 +53,7 @@ JS::Value CMessage::ToJSValCached(const ScriptInterface& scriptInterface) const { if (!m_Cached) - m_Cached.reset(new JS::PersistentRootedValue(scriptInterface.GetJSRuntime(), ToJSVal(scriptInterface))); + m_Cached.reset(new JS::PersistentRootedValue(scriptInterface.GetGeneralJSContext(), ToJSVal(scriptInterface))); return m_Cached->get(); } Index: ps/trunk/source/simulation2/scripting/ScriptComponent.cpp =================================================================== --- ps/trunk/source/simulation2/scripting/ScriptComponent.cpp +++ ps/trunk/source/simulation2/scripting/ScriptComponent.cpp @@ -23,7 +23,7 @@ #include "simulation2/serialization/IDeserializer.h" CComponentTypeScript::CComponentTypeScript(const ScriptInterface& scriptInterface, JS::HandleValue instance) : - m_ScriptInterface(scriptInterface), m_Instance(scriptInterface.GetJSRuntime(), instance) + m_ScriptInterface(scriptInterface), m_Instance(scriptInterface.GetGeneralJSContext(), instance) { // Cache the property detection for efficiency ScriptRequest rq(m_ScriptInterface); Index: ps/trunk/source/simulation2/serialization/BinarySerializer.cpp =================================================================== --- ps/trunk/source/simulation2/serialization/BinarySerializer.cpp +++ ps/trunk/source/simulation2/serialization/BinarySerializer.cpp @@ -272,7 +272,7 @@ JS::RootedValue propval(rq.cx); // Forbid getters, which might delete values and mess things up. - JS::Rooted desc(rq.cx); + JS::Rooted desc(rq.cx); if (!JS_GetPropertyDescriptorById(rq.cx, obj, id, &desc)) throw PSERROR_Serialize_ScriptError("JS_GetPropertyDescriptorById failed"); if (desc.hasGetterObject()) Index: ps/trunk/source/simulation2/serialization/StdDeserializer.cpp =================================================================== --- ps/trunk/source/simulation2/serialization/StdDeserializer.cpp +++ ps/trunk/source/simulation2/serialization/StdDeserializer.cpp @@ -30,12 +30,12 @@ CStdDeserializer::CStdDeserializer(const ScriptInterface& scriptInterface, std::istream& stream) : m_ScriptInterface(scriptInterface), m_Stream(stream) { - JS_AddExtraGCRootsTracer(m_ScriptInterface.GetJSRuntime(), CStdDeserializer::Trace, this); + JS_AddExtraGCRootsTracer(m_ScriptInterface.GetGeneralJSContext(), CStdDeserializer::Trace, this); } CStdDeserializer::~CStdDeserializer() { - JS_RemoveExtraGCRootsTracer(m_ScriptInterface.GetJSRuntime(), CStdDeserializer::Trace, this); + JS_RemoveExtraGCRootsTracer(m_ScriptInterface.GetGeneralJSContext(), CStdDeserializer::Trace, this); } void CStdDeserializer::Trace(JSTracer *trc, void *data) Index: ps/trunk/source/simulation2/system/ComponentManager.h =================================================================== --- ps/trunk/source/simulation2/system/ComponentManager.h +++ ps/trunk/source/simulation2/system/ComponentManager.h @@ -74,7 +74,7 @@ }; public: - CComponentManager(CSimContext&, shared_ptr rt, bool skipScriptFunctions = false); + CComponentManager(CSimContext&, shared_ptr cx, bool skipScriptFunctions = false); ~CComponentManager(); void LoadComponentTypes(); Index: ps/trunk/source/simulation2/system/ComponentManager.cpp =================================================================== --- ps/trunk/source/simulation2/system/ComponentManager.cpp +++ ps/trunk/source/simulation2/system/ComponentManager.cpp @@ -43,7 +43,7 @@ virtual JS::Value ToJSVal(const ScriptInterface& UNUSED(scriptInterface)) const { return msg.get(); } CMessageScripted(const ScriptInterface& scriptInterface, int mtid, const std::string& name, JS::HandleValue msg) : - mtid(mtid), handlerName("On" + name), globalHandlerName("OnGlobal" + name), msg(scriptInterface.GetJSRuntime(), msg) + mtid(mtid), handlerName("On" + name), globalHandlerName("OnGlobal" + name), msg(scriptInterface.GetGeneralJSContext(), msg) { } @@ -53,9 +53,9 @@ JS::PersistentRootedValue msg; }; -CComponentManager::CComponentManager(CSimContext& context, shared_ptr rt, bool skipScriptFunctions) : +CComponentManager::CComponentManager(CSimContext& context, shared_ptr cx, bool skipScriptFunctions) : m_NextScriptComponentTypeId(CID__LastNative), - m_ScriptInterface("Engine", "Simulation", rt), + m_ScriptInterface("Engine", "Simulation", cx), m_SimContext(context), m_CurrentlyHotloading(false) { context.SetComponentManager(this); 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().GetJSRuntime()) + m_QuickSaveMetadata(m_Simulation2.GetScriptInterface().GetGeneralJSContext()) { // 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). 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 @@ -21,6 +21,7 @@ #include "simulation2/serialization/HashSerializer.h" #include "simulation2/serialization/StdSerializer.h" #include "simulation2/serialization/StdDeserializer.h" +#include "scriptinterface/ScriptContext.h" #include "scriptinterface/ScriptInterface.h" #include "graphics/MapReader.h" @@ -841,7 +842,7 @@ LDR_BeginRegistering(); mapReader->LoadMap(L"maps/skirmishes/Greek Acropolis (2).pmp", - sim2.GetScriptInterface().GetJSRuntime(), JS::UndefinedHandleValue, + *sim2.GetScriptInterface().GetContext(), JS::UndefinedHandleValue, &terrain, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &sim2, &sim2.GetSimContext(), -1, false); LDR_EndRegistering();