1
0
forked from 0ad/0ad

Split off JSON-related function from ScriptInterface, clean up headers.

Follows 34b1920e7b.

JSON functions and ToString are movec to their own headers.
Also clean out a few PersistentRooted usage to use the 2-phase init to
clean up scriptInterface usage.

With these functions split off, we can finally clean out headers and
remove ScriptInterface.h from most of them, in favour of smaller and
more precise headers.


Take the opportunity to clarify some comments regarding Mutability.

Differential Revision: https://code.wildfiregames.com/D3961
This was SVN commit r25434.
This commit is contained in:
wraitii 2021-05-14 10:18:03 +00:00
parent f368e1a69e
commit 4f972bc623
57 changed files with 396 additions and 280 deletions

View File

@ -36,6 +36,7 @@
#include "scriptinterface/ScriptContext.h"
#include "scriptinterface/ScriptConversions.h"
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/JSON.h"
#include "simulation2/helpers/MapEdgeTiles.h"
#include <string>
@ -119,7 +120,7 @@ bool CMapGeneratorWorker::Run()
// Parse settings
JS::RootedValue settingsVal(rq.cx);
if (!m_ScriptInterface->ParseJSON(m_Settings, &settingsVal) && settingsVal.isUndefined())
if (!Script::ParseJSON(rq, m_Settings, &settingsVal) && settingsVal.isUndefined())
{
LOGERROR("CMapGeneratorWorker::Run: Failed to parse settings");
return false;

View File

@ -41,7 +41,8 @@
#include "renderer/WaterManager.h"
#include "scriptinterface/Object.h"
#include "scriptinterface/ScriptContext.h"
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/ScriptRequest.h"
#include "scriptinterface/JSON.h"
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpCinemaManager.h"
#include "simulation2/components/ICmpGarrisonHolder.h"
@ -386,7 +387,7 @@ void CMapSummaryReader::GetMapSettings(const ScriptInterface& scriptInterface, J
return;
JS::RootedValue scriptSettingsVal(rq.cx);
scriptInterface.ParseJSON(m_ScriptSettings, &scriptSettingsVal);
Script::ParseJSON(rq, m_ScriptSettings, &scriptSettingsVal);
Script::SetProperty(rq, ret, "settings", scriptSettingsVal, false);
}
@ -1280,7 +1281,7 @@ int CMapReader::GenerateMap()
scriptPath = L"maps/random/"+m_ScriptFile;
// Stringify settings to pass across threads
std::string scriptSettings = pSimulation2->GetScriptInterface().StringifyJSON(&m_ScriptSettings);
std::string scriptSettings = Script::StringifyJSON(rq, &m_ScriptSettings);
// Try to generate map
m_MapGen->GenerateMap(scriptPath, scriptSettings);

View File

@ -27,7 +27,6 @@
#include "ps/World.h"
#include "ps/CLogger.h"
#include "scriptinterface/FunctionWrapper.h"
#include "scriptinterface/ScriptInterface.h"
#include "simulation2/helpers/Position.h"
namespace JSI_GameView

View File

@ -22,7 +22,7 @@
#include "gui/CGUI.h"
#include "gui/ObjectBases/IGUIObject.h"
#include "ps/CLogger.h"
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/ScriptConversions.h"
IGUISetting::IGUISetting(const CStr& name, IGUIObject* owner) : m_pObject(*owner)
{

View File

@ -30,7 +30,7 @@
#include "scriptinterface/Object.h"
#include "scriptinterface/ScriptContext.h"
#include "scriptinterface/ScriptExtraHeaders.h"
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/ScriptConversions.h"
#include "soundmanager/ISoundManager.h"
#include <algorithm>
@ -62,7 +62,7 @@ IGUIObject::IGUIObject(CGUI& pGUI)
IGUIObject::~IGUIObject()
{
if (!m_ScriptHandlers.empty())
JS_RemoveExtraGCRootsTracer(m_pGUI.GetScriptInterface()->GetGeneralJSContext(), Trace, this);
JS_RemoveExtraGCRootsTracer(m_pGUI.GetScriptInterface().cx, Trace, this);
// m_Children is deleted along all other GUI Objects in the CGUI destructor
}
@ -310,7 +310,7 @@ void IGUIObject::RegisterScriptHandler(const CStr& eventName, const CStr& Code,
void IGUIObject::SetScriptHandler(const CStr& eventName, JS::HandleObject Function)
{
if (m_ScriptHandlers.empty())
JS_AddExtraGCRootsTracer(m_pGUI.GetScriptInterface()->GetGeneralJSContext(), Trace, this);
JS_AddExtraGCRootsTracer(m_pGUI.GetScriptInterface().cx, Trace, this);
m_ScriptHandlers[eventName] = JS::Heap<JSObject*>(Function);
@ -328,7 +328,7 @@ void IGUIObject::UnsetScriptHandler(const CStr& eventName)
m_ScriptHandlers.erase(it);
if (m_ScriptHandlers.empty())
JS_RemoveExtraGCRootsTracer(m_pGUI.GetScriptInterface()->GetGeneralJSContext(), Trace, this);
JS_RemoveExtraGCRootsTracer(m_pGUI.GetScriptInterface().cx, Trace, this);
std::unordered_map<CStr, std::vector<IGUIObject*>>::iterator it2 = m_pGUI.m_EventObjects.find(eventName);
if (it2 == m_pGUI.m_EventObjects.end())

View File

@ -44,7 +44,6 @@
#include "renderer/RenderingOptions.h"
#include "renderer/WaterManager.h"
#include "scriptinterface/Object.h"
#include "scriptinterface/ScriptInterface.h"
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpMinimap.h"
#include "simulation2/components/ICmpRangeManager.h"

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2020 Wildfire Games.
/* Copyright (C) 2021 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -18,8 +18,8 @@
#ifndef INCLUDED_JSI_GUISIZE
#define INCLUDED_JSI_GUISIZE
#include "scriptinterface/ScriptInterface.h"
#include "ps/CStr.h"
class CStr8;
class ScriptInterface;
namespace JSI_GUISize
{
@ -33,7 +33,7 @@ namespace JSI_GUISize
bool construct(JSContext* cx, uint argc, JS::Value* vp);
bool toString(JSContext* cx, uint argc, JS::Value* vp);
CStr ToPercentString(double pix, double per);
CStr8 ToPercentString(double pix, double per);
}
#endif // INCLUDED_JSI_GUISIZE

View File

@ -22,6 +22,7 @@
#include "gui/Scripting/JSInterface_GUISize.h"
#include "ps/CLogger.h"
#include "scriptinterface/Object.h"
#include "scriptinterface/ScriptInterface.h"
CGUISize::CGUISize()
: pixel(), percent()

View File

@ -21,7 +21,7 @@
#if CONFIG2_LOBBY
#include "lobby/XmppClient.h"
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/ScriptConversions.h"
template<> void Script::ToJSVal<glooxwrapper::string>(const ScriptRequest& rq, JS::MutableHandleValue ret, const glooxwrapper::string& val)
{

View File

@ -27,7 +27,6 @@
#include "ps/CStr.h"
#include "ps/Util.h"
#include "scriptinterface/FunctionWrapper.h"
#include "scriptinterface/ScriptInterface.h"
#include "third_party/encryption/pkcs5_pbkdf2.h"

View File

@ -76,7 +76,9 @@ that of Atlas depending on commandline parameters.
#include "gui/GUIManager.h"
#include "renderer/Renderer.h"
#include "rlinterface/RLInterface.h"
#include "scriptinterface/ScriptContext.h"
#include "scriptinterface/ScriptEngine.h"
#include "scriptinterface/JSON.h"
#include "simulation2/Simulation2.h"
#include "simulation2/system/TurnManager.h"
#include "soundmanager/ISoundManager.h"
@ -251,7 +253,7 @@ static void PumpEvents()
{
JS::RootedValue tmpVal(rq.cx);
Script::ToJSVal(rq, &tmpVal, ev);
std::string data = g_GUI->GetScriptInterface()->StringifyJSON(&tmpVal);
std::string data = Script::StringifyJSON(rq, &tmpVal);
PROFILE2_ATTR("%s", data.c_str());
}
in_dispatch_event(&ev);

View File

@ -37,6 +37,7 @@
#include "ps/Profile.h"
#include "ps/Threading.h"
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/JSON.h"
#include "simulation2/Simulation2.h"
#include "network/StunClient.h"
@ -375,7 +376,7 @@ std::string CNetClient::TestReadGuiMessages()
GuiPoll(&msg);
if (msg.isUndefined())
break;
r += GetScriptInterface().ToString(&msg) + "\n";
r += Script::ToString(rq, &msg) + "\n";
}
return r;
}
@ -771,7 +772,7 @@ bool CNetClient::OnGameStart(void* context, CFsmEvent* event)
const ScriptInterface& scriptInterface = client->m_Game->GetSimulation2()->GetScriptInterface();
ScriptRequest rq(scriptInterface);
JS::RootedValue initAttribs(rq.cx);
scriptInterface.ParseJSON(message->m_InitAttributes, &initAttribs);
Script::ParseJSON(rq, message->m_InitAttributes, &initAttribs);
client->m_Game->SetPlayerID(player);
client->m_Game->StartGame(&initAttribs, "");

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2020 Wildfire Games.
/* Copyright (C) 2021 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -20,7 +20,8 @@
#include "NetMessage.h"
#include "lib/utf8.h"
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/ScriptRequest.h"
#include "scriptinterface/JSON.h"
#include "simulation2/serialization/BinarySerializer.h"
#include "simulation2/serialization/StdDeserializer.h"
#include "simulation2/serialization/StdSerializer.h" // for DEBUG_SERIALIZER_ANNOTATE
@ -110,25 +111,29 @@ public:
};
CSimulationMessage::CSimulationMessage(const ScriptInterface& scriptInterface) :
CNetMessage(NMT_SIMULATION_COMMAND), m_ScriptInterface(scriptInterface), m_Data(scriptInterface.GetGeneralJSContext())
CNetMessage(NMT_SIMULATION_COMMAND), m_ScriptInterface(scriptInterface)
{
ScriptRequest rq(scriptInterface);
m_Data.init(rq.cx);
}
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.GetGeneralJSContext(), data)
m_Client(client), m_Player(player), m_Turn(turn)
{
ScriptRequest rq(scriptInterface);
m_Data.init(rq.cx, data);
}
CSimulationMessage::CSimulationMessage(const CSimulationMessage& orig) :
m_Data(orig.m_ScriptInterface.GetGeneralJSContext()),
m_Client(orig.m_Client),
m_Player(orig.m_Player),
m_ScriptInterface(orig.m_ScriptInterface),
m_Turn(orig.m_Turn),
CNetMessage(orig)
{
m_Data = orig.m_Data;
ScriptRequest rq(m_ScriptInterface);
m_Data.init(rq.cx, orig.m_Data);
}
u8* CSimulationMessage::Serialize(u8* pBuffer) const
@ -176,7 +181,7 @@ size_t CSimulationMessage::GetSerializedLength() const
CStr CSimulationMessage::ToString() const
{
std::string source = m_ScriptInterface.ToString(const_cast<JS::PersistentRootedValue*>(&m_Data));
std::string source = Script::ToString(ScriptRequest(m_ScriptInterface), const_cast<JS::PersistentRootedValue*>(&m_Data));
std::stringstream stream;
stream << "CSimulationMessage { m_Client: " << m_Client << ", m_Player: " << m_Player << ", m_Turn: " << m_Turn << ", m_Data: " << source << " }";
@ -185,14 +190,17 @@ CStr CSimulationMessage::ToString() const
CGameSetupMessage::CGameSetupMessage(const ScriptInterface& scriptInterface) :
CNetMessage(NMT_GAME_SETUP), m_ScriptInterface(scriptInterface), m_Data(scriptInterface.GetGeneralJSContext())
CNetMessage(NMT_GAME_SETUP), m_ScriptInterface(scriptInterface)
{
ScriptRequest rq(m_ScriptInterface);
m_Data.init(rq.cx);
}
CGameSetupMessage::CGameSetupMessage(const ScriptInterface& scriptInterface, JS::HandleValue data) :
CNetMessage(NMT_GAME_SETUP), m_ScriptInterface(scriptInterface),
m_Data(scriptInterface.GetGeneralJSContext(), data)
CNetMessage(NMT_GAME_SETUP), m_ScriptInterface(scriptInterface)
{
ScriptRequest rq(m_ScriptInterface);
m_Data.init(rq.cx, data);
}
u8* CGameSetupMessage::Serialize(u8* pBuffer) const
@ -223,7 +231,7 @@ size_t CGameSetupMessage::GetSerializedLength() const
CStr CGameSetupMessage::ToString() const
{
std::string source = m_ScriptInterface.ToString(const_cast<JS::PersistentRootedValue*>(&m_Data));
std::string source = Script::ToString(ScriptRequest(m_ScriptInterface), const_cast<JS::PersistentRootedValue*>(&m_Data));
std::stringstream stream;
stream << "CGameSetupMessage { m_Data: " << source << " }";

View File

@ -35,6 +35,7 @@
#include "ps/Threading.h"
#include "scriptinterface/ScriptContext.h"
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/JSON.h"
#include "simulation2/Simulation2.h"
#include "simulation2/system/TurnManager.h"
@ -129,7 +130,7 @@ public:
// Send the init attributes alongside - these should be correct since the game should be started.
CJoinSyncStartMessage message;
message.m_InitAttributes = m_Server.GetScriptInterface().StringifyJSON(&m_Server.m_InitAttributes);
message.m_InitAttributes = Script::StringifyJSON(ScriptRequest(m_Server.GetScriptInterface()), &m_Server.m_InitAttributes);
session->SendMessage(&message);
}
@ -423,7 +424,7 @@ void CNetServerWorker::Run()
// Implement autostart mode
if (m_State == SERVER_STATE_PREGAME && (int)m_PlayerAssignments.size() == m_AutostartPlayers)
StartGame(m_ScriptInterface->StringifyJSON(&m_InitAttributes));
StartGame(Script::StringifyJSON(ScriptRequest(m_ScriptInterface), &m_InitAttributes));
// Update profiler stats
m_Stats->LatchHostState(m_Host);
@ -469,7 +470,7 @@ bool CNetServerWorker::RunStep()
else
{
JS::RootedValue gameAttributesVal(rq.cx);
GetScriptInterface().ParseJSON(newGameAttributes.back(), &gameAttributesVal);
Script::ParseJSON(rq, newGameAttributes.back(), &gameAttributesVal);
m_InitAttributes = gameAttributesVal;
}
}
@ -1550,7 +1551,7 @@ void CNetServerWorker::StartGame(const CStr& initAttribs)
SendPlayerAssignments();
// Update init attributes. They should no longer change.
m_ScriptInterface->ParseJSON(initAttribs, &m_InitAttributes);
Script::ParseJSON(ScriptRequest(m_ScriptInterface), initAttribs, &m_InitAttributes);
CGameStartMessage gameStart;
gameStart.m_InitAttributes = initAttribs;
@ -1697,11 +1698,11 @@ void CNetServer::StartGame()
m_Worker->m_StartGameQueue.push_back(true);
}
void CNetServer::UpdateInitAttributes(JS::MutableHandleValue attrs, const ScriptInterface& scriptInterface)
void CNetServer::UpdateInitAttributes(JS::MutableHandleValue attrs, const ScriptRequest& rq)
{
// Pass the attributes as JSON, since that's the easiest safe
// cross-thread way of passing script data
std::string attrsJSON = scriptInterface.StringifyJSON(attrs, false);
std::string attrsJSON = Script::StringifyJSON(rq, attrs, false);
std::lock_guard<std::mutex> lock(m_Worker->m_WorkerMutex);
m_Worker->m_InitAttributesQueue.push_back(attrsJSON);

View File

@ -35,10 +35,11 @@
class CNetServerSession;
class CNetServerTurnManager;
class CFsmEvent;
class ScriptInterface;
class CPlayerAssignmentMessage;
class CNetStatsTable;
class CSimulationMessage;
class ScriptInterface;
class ScriptRequest;
class CNetServerWorker;
@ -132,9 +133,9 @@ public:
/**
* Call from the GUI to update the game setup attributes.
* The changes won't be propagated to clients until game start.
* @param attrs init attributes, in the script context of scriptInterface
* @param attrs init attributes, in the script context of rq
*/
void UpdateInitAttributes(JS::MutableHandleValue attrs, const ScriptInterface& scriptInterface);
void UpdateInitAttributes(JS::MutableHandleValue attrs, const ScriptRequest& rq);
/**
* Set the turn length to a fixed value.

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2019 Wildfire Games.
/* Copyright (C) 2021 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -25,7 +25,6 @@
#include "NetStats.h"
#include "ps/CLogger.h"
#include "ps/Profile.h"
#include "scriptinterface/ScriptInterface.h"
constexpr int NETCLIENT_POLL_TIMEOUT = 50;

View File

@ -33,6 +33,7 @@
#include "ps/Util.h"
#include "scriptinterface/FunctionWrapper.h"
#include "scriptinterface/StructuredClone.h"
#include "scriptinterface/JSON.h"
#include "third_party/encryption/pkcs5_pbkdf2.h"
@ -275,7 +276,7 @@ void StartNetworkGame(const ScriptInterface& scriptInterface, JS::HandleValue at
// TODO: This is a workaround because we need to pass a MutableHandle to a JSAPI functions somewhere (with no obvious reason).
ScriptRequest rq(scriptInterface);
JS::RootedValue attribs(rq.cx, attribs1);
g_NetClient->SendStartGameMessage(scriptInterface.StringifyJSON(&attribs));
g_NetClient->SendStartGameMessage(Script::StringifyJSON(rq, &attribs));
}
void SetTurnLength(int length)

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2020 Wildfire Games.
/* Copyright (C) 2021 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -24,5 +24,4 @@
#include "ps/CStr.h"
#include "ps/ThreadUtil.h"
#include "network/NetMessage.h"
#include "scriptinterface/ScriptInterface.h"
#endif // CONFIG_ENABLE_PCH

View File

@ -45,6 +45,7 @@
#include "ps/Pyrogenesis.h"
#include "renderer/Renderer.h"
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/JSON.h"
CConsole* g_Console = 0;
@ -560,7 +561,7 @@ void CConsole::ProcessBuffer(const wchar_t* szLine)
JS::RootedValue rval(rq.cx);
pScriptInterface->Eval(CStrW(szLine).ToUTF8().c_str(), &rval);
if (!rval.isUndefined())
InsertMessage(pScriptInterface->ToString(&rval));
InsertMessage(Script::ToString(rq, &rval));
}
void CConsole::LoadHistory()

View File

@ -43,6 +43,7 @@
#include "renderer/WaterManager.h"
#include "scriptinterface/FunctionWrapper.h"
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/JSON.h"
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpPlayer.h"
#include "simulation2/components/ICmpPlayerManager.h"
@ -196,7 +197,7 @@ bool CGame::StartVisualReplay(const OsPath& replayPath)
ScriptRequest rq(scriptInterface);
JS::RootedValue attribs(rq.cx);
scriptInterface.ParseJSON(line, &attribs);
Script::ParseJSON(rq, line, &attribs);
StartGame(&attribs, "");
return true;

View File

@ -83,6 +83,7 @@
#include "scriptinterface/ScriptStats.h"
#include "scriptinterface/ScriptContext.h"
#include "scriptinterface/ScriptConversions.h"
#include "scriptinterface/JSON.h"
#include "simulation2/Simulation2.h"
#include "lobby/IXmppClient.h"
#include "soundmanager/scripting/JSInterface_Sound.h"
@ -1255,7 +1256,7 @@ bool Autostart(const CmdLineArgs& args)
// 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(rq.cx);
scriptInterface.ReadJSONFile(scriptPath, &scriptData);
Script::ReadJSONFile(rq, scriptPath, &scriptData);
if (!scriptData.isUndefined() && Script::GetProperty(rq, scriptData, "settings", &settings))
{
// JSON loaded ok - copy script name over to game attributes
@ -1307,7 +1308,7 @@ bool Autostart(const CmdLineArgs& args)
// (e.g. name of map) are always present, even when autostart is
// partially configured
CStr8 mapSettingsJSON = LoadSettingsOfScenarioMap("maps/" + autoStartName + ".xml");
scriptInterface.ParseJSON(mapSettingsJSON, &settings);
Script::ParseJSON(rq, mapSettingsJSON, &settings);
// Initialize the playerData array being modified by autostart
// with the real map data, so sensible values are present:
@ -1518,7 +1519,7 @@ bool Autostart(const CmdLineArgs& args)
JS::RootedValue victoryScripts(rq.cx);
CStrW scriptPath = L"simulation/data/settings/victory_conditions/" + victory.FromUTF8() + L".json";
scriptInterface.ReadJSONFile(scriptPath, &scriptData);
Script::ReadJSONFile(rq, scriptPath, &scriptData);
if (!scriptData.isUndefined() && Script::GetProperty(rq, scriptData, "Data", &data) && !data.isUndefined()
&& Script::GetProperty(rq, data, "Scripts", &victoryScripts) && !victoryScripts.isUndefined())
{

View File

@ -45,7 +45,7 @@
#include "ps/VideoMode.h"
#include "scriptinterface/FunctionWrapper.h"
#include "scriptinterface/Object.h"
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/JSON.h"
#if OS_LINUX
#include <fstream>
@ -222,8 +222,8 @@ void RunHardwareDetection()
g_UserReporter.SubmitReport(
"hwdetect",
reportVersion,
scriptInterface.StringifyJSON(&settings, false),
scriptInterface.StringifyJSON(&settings, true));
Script::StringifyJSON(rq, &settings, false),
Script::StringifyJSON(rq, &settings, true));
// Run the detection script:
JS::RootedValue global(rq.cx, rq.globalValue());

View File

@ -28,6 +28,7 @@
#include "ps/Pyrogenesis.h"
#include "scriptinterface/Object.h"
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/JSON.h"
#include <algorithm>
#include <boost/algorithm/string/split.hpp>
@ -74,7 +75,7 @@ JS::Value Mod::GetAvailableMods(const ScriptInterface& scriptInterface)
continue;
JS::RootedValue json(rq.cx);
if (!scriptInterface.ParseJSON(modinfo.GetAsString(), &json))
if (!Script::ParseJSON(rq, modinfo.GetAsString(), &json))
continue;
// Valid mod, add it to our structure
@ -101,7 +102,7 @@ JS::Value Mod::GetAvailableMods(const ScriptInterface& scriptInterface)
continue;
JS::RootedValue json(rq.cx);
if (!scriptInterface.ParseJSON(modinfo.GetAsString(), &json))
if (!Script::ParseJSON(rq, modinfo.GetAsString(), &json))
continue;
// Valid mod, add it to our structure

View File

@ -20,7 +20,6 @@
#include "ps/CStr.h"
#include "ps/GameSetup/CmdLineArgs.h"
#include "scriptinterface/ScriptInterface.h"
class ScriptContext;

View File

@ -22,6 +22,8 @@
#include "lib/file/vfs/vfs_util.h"
#include "ps/Filesystem.h"
#include "ps/XML/Xeromyces.h"
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/JSON.h"
#ifdef OS_WIN
#include <fstream>
@ -69,7 +71,7 @@ CModInstaller::ModInstallationResult CModInstaller::Install(
ScriptRequest rq(scriptInterface);
JS::RootedValue json_val(rq.cx);
if (!scriptInterface.ParseJSON(modinfo.GetAsString(), &json_val))
if (!Script::ParseJSON(rq, modinfo.GetAsString(), &json_val))
return FAIL_ON_PARSE_JSON;
JS::RootedObject json_obj(rq.cx, json_val.toObjectOrNull());
JS::RootedValue name_val(rq.cx);

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2018 Wildfire Games.
/* Copyright (C) 2021 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -20,10 +20,11 @@
#include "CStr.h"
#include "lib/file/vfs/vfs.h"
#include "scriptinterface/ScriptInterface.h"
#include <vector>
class ScriptContext;
/**
* Install a mod into the mods directory.
*/

View File

@ -36,7 +36,9 @@
#include "ps/ModInstaller.h"
#include "ps/Util.h"
#include "scriptinterface/ScriptConversions.h"
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/ScriptContext.h"
#include "scriptinterface/ScriptRequest.h"
#include "scriptinterface/JSON.h"
#include <boost/algorithm/string.hpp>
#include <iomanip>
@ -589,7 +591,7 @@ bool ModIo::ParseGameIdResponse(const ScriptInterface& scriptInterface, const st
JS::RootedValue gameResponse(rq.cx);
if (!scriptInterface.ParseJSON(responseData, &gameResponse))
if (!Script::ParseJSON(rq, responseData, &gameResponse))
FAIL("Failed to parse response as JSON.");
if (!gameResponse.isObject())
@ -660,7 +662,7 @@ bool ModIo::ParseModsResponse(const ScriptInterface& scriptInterface, const std:
JS::RootedValue modResponse(rq.cx);
if (!scriptInterface.ParseJSON(responseData, &modResponse))
if (!Script::ParseJSON(rq, responseData, &modResponse))
FAIL("Failed to parse response as JSON.");
if (!modResponse.isObject())
@ -749,7 +751,7 @@ bool ModIo::ParseModsResponse(const ScriptInterface& scriptInterface, const std:
INVALIDATE_DATA_AND_CONTINUE("Failed to get metadata_blob from modFile.");
JS::RootedValue metadata(rq.cx);
if (!scriptInterface.ParseJSON(metadata_blob, &metadata))
if (!Script::ParseJSON(rq, metadata_blob, &metadata))
INVALIDATE_DATA_AND_CONTINUE("Failed to parse metadata_blob as JSON.");
if (!metadata.isObject())

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2019 Wildfire Games.
/* Copyright (C) 2021 Wildfire Games.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@ -25,7 +25,6 @@
#include "lib/external_libraries/curl.h"
#include "lib/os_path.h"
#include "scriptinterface/ScriptInterface.h"
#include <sodium.h>
#include <string>

View File

@ -36,7 +36,6 @@
#include "ps/Pyrogenesis.h"
#include "renderer/Renderer.h"
#include "scriptinterface/Object.h"
#include "scriptinterface/ScriptInterface.h"
#include <algorithm>
#include <fstream>
@ -492,16 +491,19 @@ namespace
const ScriptInterface& m_ScriptInterface;
JS::PersistentRooted<JS::Value> m_Root;
DumpTable(const ScriptInterface& scriptInterface, JS::HandleValue root) :
m_ScriptInterface(scriptInterface), m_Root(scriptInterface.GetGeneralJSContext(), root)
m_ScriptInterface(scriptInterface)
{
ScriptRequest rq(scriptInterface);
m_Root.init(rq.cx, root);
}
// std::for_each requires a move constructor and the use of JS::PersistentRooted<T> apparently breaks a requirement for an
// automatic move constructor
DumpTable(DumpTable && original) :
m_ScriptInterface(original.m_ScriptInterface),
m_Root(original.m_ScriptInterface.GetGeneralJSContext(), original.m_Root.get())
m_ScriptInterface(original.m_ScriptInterface)
{
ScriptRequest rq(m_ScriptInterface);
m_Root.init(rq.cx, original.m_Root.get());
}
void operator() (AbstractProfileTable* table)

View File

@ -36,8 +36,9 @@
#include "ps/VisualReplay.h"
#include "scriptinterface/Object.h"
#include "scriptinterface/ScriptContext.h"
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/ScriptRequest.h"
#include "scriptinterface/ScriptStats.h"
#include "scriptinterface/JSON.h"
#include "simulation2/components/ICmpGuiInterface.h"
#include "simulation2/helpers/Player.h"
#include "simulation2/helpers/SimulationCommand.h"
@ -79,7 +80,7 @@ void CReplayLogger::StartGame(JS::MutableHandleValue attribs)
debug_printf("Writing replay to %s\n", m_Directory.string8().c_str());
m_Stream = new std::ofstream(OsString(m_Directory / L"commands.txt").c_str(), std::ofstream::out | std::ofstream::trunc);
*m_Stream << "start " << m_ScriptInterface.StringifyJSON(attribs, false) << "\n";
*m_Stream << "start " << Script::StringifyJSON(rq, attribs, false) << "\n";
}
void CReplayLogger::Turn(u32 n, u32 turnLength, std::vector<SimulationCommand>& commands)
@ -89,7 +90,7 @@ void CReplayLogger::Turn(u32 n, u32 turnLength, std::vector<SimulationCommand>&
*m_Stream << "turn " << n << " " << turnLength << "\n";
for (SimulationCommand& command : commands)
*m_Stream << "cmd " << command.player << " " << m_ScriptInterface.StringifyJSON(&command.data, false) << "\n";
*m_Stream << "cmd " << command.player << " " << Script::StringifyJSON(rq, &command.data, false) << "\n";
*m_Stream << "end\n";
m_Stream->flush();
@ -123,7 +124,7 @@ void CReplayLogger::SaveMetadata(const CSimulation2& simulation)
CreateDirectories(fileName.Parent(), 0700);
std::ofstream stream (OsString(fileName).c_str(), std::ofstream::out | std::ofstream::trunc);
stream << scriptInterface.StringifyJSON(&metadata, false);
stream << Script::StringifyJSON(rq, &metadata, false);
stream.close();
debug_printf("Saved replay metadata to %s\n", fileName.string8().c_str());
}
@ -240,7 +241,7 @@ void CReplayPlayer::Replay(const bool serializationtest, const int rejointesttur
std::string line;
std::getline(*m_Stream, line);
JS::RootedValue attribs(rq.cx);
ENSURE(g_Game->GetSimulation2()->GetScriptInterface().ParseJSON(line, &attribs));
ENSURE(Script::ParseJSON(rq, line, &attribs));
CheckReplayMods(g_Game->GetSimulation2()->GetScriptInterface(), attribs);
@ -265,7 +266,7 @@ void CReplayPlayer::Replay(const bool serializationtest, const int rejointesttur
std::string line;
std::getline(*m_Stream, line);
JS::RootedValue data(rq.cx);
g_Game->GetSimulation2()->GetScriptInterface().ParseJSON(line, &data);
Script::ParseJSON(rq, line, &data);
Script::FreezeObject(rq, data, true);
commands.emplace_back(SimulationCommand(player, rq.cx, data));
}

View File

@ -32,6 +32,7 @@
#include "ps/Mod.h"
#include "ps/Pyrogenesis.h"
#include "scriptinterface/Object.h"
#include "scriptinterface/JSON.h"
#include "scriptinterface/StructuredClone.h"
#include "simulation2/Simulation2.h"
@ -117,7 +118,7 @@ Status SavedGames::Save(const CStrW& name, const CStrW& description, CSimulation
Script::SetProperty(rq, metadata, "gui", guiMetadata);
Script::SetProperty(rq, metadata, "description", description);
std::string metadataString = simulation.GetScriptInterface().StringifyJSON(&metadata, true);
std::string metadataString = Script::StringifyJSON(rq, &metadata, true);
// Write the saved game as zip file containing the various components
PIArchiveWriter archiveWriter = CreateArchiveWriter_Zip(tempSaveFileRealPath, false);
@ -163,9 +164,10 @@ public:
*/
CGameLoader(const ScriptInterface& scriptInterface, std::string* savedState) :
m_ScriptInterface(scriptInterface),
m_Metadata(scriptInterface.GetGeneralJSContext()),
m_SavedState(savedState)
{
ScriptRequest rq(scriptInterface);
m_Metadata.init(rq.cx);
}
static void ReadEntryCallback(const VfsPath& pathname, const CFileInfo& fileInfo, PIArchiveFile archiveFile, uintptr_t cbData)
@ -180,7 +182,7 @@ public:
std::string buffer;
buffer.resize(fileInfo.Size());
WARN_IF_ERR(archiveFile->Load("", DummySharedPtr((u8*)buffer.data()), buffer.size()));
m_ScriptInterface.ParseJSON(buffer, &m_Metadata);
Script::ParseJSON(ScriptRequest(m_ScriptInterface), buffer, &m_Metadata);
}
else if (pathname == L"simulation.dat" && m_SavedState)
{

View File

@ -33,8 +33,7 @@
#include "ps/Pyrogenesis.h"
#include "ps/Replay.h"
#include "ps/Util.h"
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/ScriptExtraHeaders.h"
#include "scriptinterface/JSON.h"
#include <fstream>
@ -85,7 +84,7 @@ bool VisualReplay::ReadCacheFile(const ScriptInterface& scriptInterface, JS::Mut
ScriptRequest rq(scriptInterface);
JS::RootedValue cachedReplays(rq.cx);
if (scriptInterface.ParseJSON(cacheStr, &cachedReplays))
if (Script::ParseJSON(rq, cacheStr, &cachedReplays))
{
cachedReplaysObject.set(&cachedReplays.toObject());
bool isArray;
@ -104,7 +103,7 @@ void VisualReplay::StoreCacheFile(const ScriptInterface& scriptInterface, JS::Ha
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 << Script::StringifyJSON(rq, &replaysRooted);
cacheStream.close();
wunlink(GetCacheFilePath());
@ -375,7 +374,7 @@ JS::Value VisualReplay::LoadReplayData(const ScriptInterface& scriptInterface, c
std::getline(*replayStream, header);
ScriptRequest rq(scriptInterface);
JS::RootedValue attribs(rq.cx);
if (!scriptInterface.ParseJSON(header, &attribs))
if (!Script::ParseJSON(rq, header, &attribs))
{
LOGERROR("Couldn't parse replay header of %s", replayFile.string8().c_str());
SAFE_DELETE(replayStream);
@ -450,7 +449,7 @@ JS::Value VisualReplay::GetReplayAttributes(const ScriptInterface& scriptInterfa
// Read and return first line
std::getline(*replayStream, line);
scriptInterface.ParseJSON(line, &attribs);
Script::ParseJSON(rq, line, &attribs);
SAFE_DELETE(replayStream);;
return attribs;
}
@ -502,7 +501,7 @@ JS::Value VisualReplay::GetReplayMetadata(const ScriptInterface& scriptInterface
std::getline(*stream, line);
stream->close();
SAFE_DELETE(stream);
scriptInterface.ParseJSON(line, &metadata);
Script::ParseJSON(rq, line, &metadata);
return metadata;
}

View File

@ -31,7 +31,6 @@
#include "ps/Hotkey.h"
#include "ps/Util.h"
#include "scriptinterface/FunctionWrapper.h"
#include "scriptinterface/ScriptInterface.h"
#include "tools/atlas/GameInterface/GameLoop.h"
extern void QuitEngine();

View File

@ -21,7 +21,6 @@
#include "ps/Mod.h"
#include "scriptinterface/FunctionWrapper.h"
#include "scriptinterface/ScriptInterface.h"
extern void RestartEngine();

View File

@ -24,8 +24,7 @@
#include "ps/CStr.h"
#include "ps/Filesystem.h"
#include "scriptinterface/FunctionWrapper.h"
#include "scriptinterface/ScriptExtraHeaders.h"
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/JSON.h"
#include <sstream>
@ -208,7 +207,7 @@ JS::Value ReadJSONFile(const ScriptInterface& scriptInterface, const std::vector
return JS::NullValue();
JS::RootedValue out(rq.cx);
scriptInterface.ReadJSONFile(filePath, &out);
Script::ReadJSONFile(rq, filePath, &out);
return out;
}
@ -220,7 +219,7 @@ void WriteJSONFile(const ScriptInterface& scriptInterface, const std::wstring& f
// TODO: This is a workaround because we need to pass a MutableHandle to StringifyJSON.
JS::RootedValue val(rq.cx, val1);
std::string str(scriptInterface.StringifyJSON(&val, false));
std::string str(Script::StringifyJSON(rq, &val, false));
VfsPath path(filePath);
WriteBuffer buf;

View File

@ -24,6 +24,7 @@
#include "ps/CLogger.h"
#include "ps/Mod.h"
#include "scriptinterface/JSON.h"
#include "scriptinterface/ScriptInterface.h"
class TestMod : public CxxTest::TestSuite
@ -99,7 +100,7 @@ public:
}\
";
JS::RootedValue json(rq.cx);
TS_ASSERT(script.ParseJSON(jsonString, &json));
TS_ASSERT(Script::ParseJSON(rq, jsonString, &json));
JS_SetProperty(rq.cx, obj, "public", json);
JS::RootedValue jsonW(rq.cx);
@ -112,7 +113,7 @@ public:
\"dependencies\" : [\"0ad=0.0.24\"]\
}\
";
TS_ASSERT(script.ParseJSON(jsonStringW, &jsonW));
TS_ASSERT(Script::ParseJSON(rq, jsonStringW, &jsonW));
JS_SetProperty(rq.cx, obj, "wrong", jsonW);
JS::RootedValue jsonG(rq.cx);
@ -125,7 +126,7 @@ public:
\"dependencies\" : [\"0ad=0.0.25\"]\
}\
";
TS_ASSERT(script.ParseJSON(jsonStringG, &jsonG));
TS_ASSERT(Script::ParseJSON(rq, jsonStringG, &jsonG));
JS_SetProperty(rq.cx, obj, "good", jsonG);
JS::RootedValue jsonG2(rq.cx);
@ -138,7 +139,7 @@ public:
\"dependencies\" : [\"0ad>=0.0.24\"]\
}\
";
TS_ASSERT(script.ParseJSON(jsonStringG2, &jsonG2));
TS_ASSERT(Script::ParseJSON(rq, jsonStringG2, &jsonG2));
JS_SetProperty(rq.cx, obj, "good2", jsonG2);
JS::RootedValue availableMods(rq.cx, JS::ObjectValue(*obj));

View File

@ -28,6 +28,7 @@
#include "ps/Loader.h"
#include "scriptinterface/Object.h"
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/JSON.h"
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpAIInterface.h"
#include "simulation2/components/ICmpTemplateManager.h"
@ -342,7 +343,7 @@ void Interface::ApplyMessage(const GameMessage& msg)
ScriptInterface& scriptInterface = g_Game->GetSimulation2()->GetScriptInterface();
ScriptRequest rq(scriptInterface);
JS::RootedValue attrs(rq.cx);
scriptInterface.ParseJSON(m_ScenarioConfig.content, &attrs);
Script::ParseJSON(rq, m_ScenarioConfig.content, &attrs);
g_Game->SetPlayerID(m_ScenarioConfig.playerID);
g_Game->StartGame(&attrs, "");
@ -387,7 +388,7 @@ void Interface::ApplyMessage(const GameMessage& msg)
{
ScriptRequest rq(scriptInterface);
JS::RootedValue commandJSON(rq.cx);
scriptInterface.ParseJSON(command.json_cmd, &commandJSON);
Script::ParseJSON(rq, command.json_cmd, &commandJSON);
turnMgr->PostCommand(command.playerID, commandJSON);
}
@ -406,24 +407,24 @@ void Interface::ApplyMessage(const GameMessage& msg)
m_MsgLock.unlock();
break;
}
case GameMessageType::Evaluate:
{
if (!g_Game)
{
m_ReturnValue = EMPTY_STATE;
m_MsgApplied.notify_one();
m_MsgLock.unlock();
return;
}
const ScriptInterface& scriptInterface = g_Game->GetSimulation2()->GetScriptInterface();
ScriptRequest rq(scriptInterface);
JS::RootedValue ret(rq.cx);
scriptInterface.Eval(m_Code.c_str(), &ret);
m_ReturnValue = scriptInterface.StringifyJSON(&ret, false);
m_MsgApplied.notify_one();
m_MsgLock.unlock();
break;
}
case GameMessageType::Evaluate:
{
if (!g_Game)
{
m_ReturnValue = EMPTY_STATE;
m_MsgApplied.notify_one();
m_MsgLock.unlock();
return;
}
const ScriptInterface& scriptInterface = g_Game->GetSimulation2()->GetScriptInterface();
ScriptRequest rq(scriptInterface);
JS::RootedValue ret(rq.cx);
scriptInterface.Eval(m_Code.c_str(), &ret);
m_ReturnValue = Script::StringifyJSON(rq, &ret, false);
m_MsgApplied.notify_one();
m_MsgLock.unlock();
break;
}
default:
break;
}
@ -437,7 +438,7 @@ std::string Interface::GetGameState() const
ScriptRequest rq(scriptInterface);
JS::RootedValue state(rq.cx);
cmpAIInterface->GetFullRepresentation(&state, true);
return scriptInterface.StringifyJSON(&state, false);
return Script::StringifyJSON(rq, &state, false);
}
bool Interface::IsGameRunning() const

View File

@ -0,0 +1,141 @@
/* Copyright (C) 2021 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "JSON.h"
#include "ps/CStr.h"
#include "ps/Filesystem.h"
#include "ps/utf16string.h"
#include "scriptinterface/FunctionWrapper.h"
#include "scriptinterface/ScriptExceptions.h"
#include "scriptinterface/ScriptRequest.h"
#include "scriptinterface/ScriptTypes.h"
// Ignore warnings in SM headers.
#if GCC_VERSION || CLANG_VERSION
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wunused-parameter"
# pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
#elif MSC_VERSION
# pragma warning(push, 1)
#endif
#include "js/JSON.h"
#if GCC_VERSION || CLANG_VERSION
# pragma GCC diagnostic pop
#elif MSC_VERSION
# pragma warning(pop)
#endif
bool Script::ParseJSON(const ScriptRequest& rq, const std::string& string_utf8, JS::MutableHandleValue out)
{
std::wstring attrsW = wstring_from_utf8(string_utf8);
utf16string string(attrsW.begin(), attrsW.end());
if (JS_ParseJSON(rq.cx, reinterpret_cast<const char16_t*>(string.c_str()), (u32)string.size(), out))
return true;
ScriptException::CatchPending(rq);
return false;
}
void Script::ReadJSONFile(const ScriptRequest& rq, const VfsPath& path, JS::MutableHandleValue out)
{
if (!VfsFileExists(path))
{
LOGERROR("File '%s' does not exist", path.string8());
return;
}
CVFSFile file;
PSRETURN ret = file.Load(g_VFS, path);
if (ret != PSRETURN_OK)
{
LOGERROR("Failed to load file '%s': %s", path.string8(), GetErrorString(ret));
return;
}
std::string content(file.DecodeUTF8()); // assume it's UTF-8
if (!ParseJSON(rq, content, out))
LOGERROR("Failed to parse '%s'", path.string8());
}
namespace
{
struct Stringifier
{
static bool callback(const char16_t* buf, u32 len, void* data)
{
utf16string str(buf, buf+len);
std::wstring strw(str.begin(), str.end());
Status err; // ignore Unicode errors
static_cast<Stringifier*>(data)->stream << utf8_from_wstring(strw, &err);
return true;
}
std::stringstream stream;
};
} // anonymous namespace
// JS_Stringify takes a mutable object because ToJSON may arbitrarily mutate the value.
// TODO: it'd be nice to have a non-mutable variant.
std::string Script::StringifyJSON(const ScriptRequest& rq, JS::MutableHandleValue obj, bool indent)
{
Stringifier str;
JS::RootedValue indentVal(rq.cx, indent ? JS::Int32Value(2) : JS::UndefinedValue());
if (!JS_Stringify(rq.cx, obj, nullptr, indentVal, &Stringifier::callback, &str))
{
ScriptException::CatchPending(rq);
return std::string();
}
return str.stream.str();
}
std::string Script::ToString(const ScriptRequest& rq, JS::MutableHandleValue obj, bool pretty)
{
if (obj.isUndefined())
return "(void 0)";
// Try to stringify as JSON if possible
// (TODO: this is maybe a bad idea since it'll drop 'undefined' values silently)
if (pretty)
{
Stringifier str;
JS::RootedValue indentVal(rq.cx, JS::Int32Value(2));
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
JS_ClearPendingException(rq.cx);
}
// Caller didn't want pretty output, or JSON conversion failed (e.g. due to cycles),
// so fall back to obj.toSource()
std::wstring source = L"(error)";
ScriptFunction::Call(rq, obj, "toSource", source);
return utf8_from_wstring(source);
}

View File

@ -0,0 +1,60 @@
/* Copyright (C) 2021 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef INCLUDED_SCRIPTINTERFACE_JSON
#define INCLUDED_SCRIPTINTERFACE_JSON
#include "ScriptForward.h"
#include <string>
class Path;
using VfsPath = Path;
class ScriptRequest;
/**
* @file JSON.h
* Contains JSON and more generally object-string conversion functions.
*/
namespace Script
{
/**
* Convert an object to a UTF-8 encoded string, either with JSON
* (if pretty == true and there is no JSON error) or with toSource().
*/
std::string ToString(const ScriptRequest& rq, JS::MutableHandleValue obj, bool pretty = false);
/**
* Parse a UTF-8-encoded JSON string. Returns the unmodified value on error
* and prints an error message.
* @return true on success; false otherwise
*/
bool ParseJSON(const ScriptRequest& rq, const std::string& string_utf8, JS::MutableHandleValue out);
/**
* Read a JSON file. Returns the unmodified value on error and prints an error message.
*/
void ReadJSONFile(const ScriptRequest& rq, const VfsPath& path, JS::MutableHandleValue out);
/**
* Stringify to a JSON string, UTF-8 encoded. Returns an empty string on error.
*/
std::string StringifyJSON(const ScriptRequest& rq, JS::MutableHandleValue obj, bool indent = true);
}
#endif // INCLUDED_SCRIPTINTERFACE_JSON

View File

@ -96,4 +96,8 @@ private:
double m_LastGCCheck;
};
// Using a global object for the context is a workaround until Simulation, AI, etc,
// use their own threads and also their own contexts.
extern thread_local std::shared_ptr<ScriptContext> g_ScriptContext;
#endif // INCLUDED_SCRIPTCONTEXT

View File

@ -56,7 +56,6 @@
#include "js/ForOfIterator.h"
#include "js/GCAPI.h"
#include "js/GCHashTable.h"
#include "js/JSON.h"
#include "js/SourceText.h"
#include "js/Proxy.h"
#include "js/Warnings.h"

View File

@ -682,100 +682,3 @@ bool ScriptInterface::Eval(const char* code, JS::MutableHandleValue rval) const
ScriptException::CatchPending(rq);
return false;
}
bool ScriptInterface::ParseJSON(const std::string& string_utf8, JS::MutableHandleValue out) const
{
ScriptRequest rq(this);
std::wstring attrsW = wstring_from_utf8(string_utf8);
utf16string string(attrsW.begin(), attrsW.end());
if (JS_ParseJSON(rq.cx, reinterpret_cast<const char16_t*>(string.c_str()), (u32)string.size(), out))
return true;
ScriptException::CatchPending(rq);
return false;
}
void ScriptInterface::ReadJSONFile(const VfsPath& path, JS::MutableHandleValue out) const
{
if (!VfsFileExists(path))
{
LOGERROR("File '%s' does not exist", path.string8());
return;
}
CVFSFile file;
PSRETURN ret = file.Load(g_VFS, path);
if (ret != PSRETURN_OK)
{
LOGERROR("Failed to load file '%s': %s", path.string8(), GetErrorString(ret));
return;
}
std::string content(file.DecodeUTF8()); // assume it's UTF-8
if (!ParseJSON(content, out))
LOGERROR("Failed to parse '%s'", path.string8());
}
struct Stringifier
{
static bool callback(const char16_t* buf, u32 len, void* data)
{
utf16string str(buf, buf+len);
std::wstring strw(str.begin(), str.end());
Status err; // ignore Unicode errors
static_cast<Stringifier*>(data)->stream << utf8_from_wstring(strw, &err);
return true;
}
std::stringstream stream;
};
// TODO: It's not quite clear why JS_Stringify needs JS::MutableHandleValue. |obj| should not get modified.
// It probably has historical reasons and could be changed by SpiderMonkey in the future.
std::string ScriptInterface::StringifyJSON(JS::MutableHandleValue obj, bool indent) const
{
ScriptRequest rq(this);
Stringifier str;
JS::RootedValue indentVal(rq.cx, indent ? JS::Int32Value(2) : JS::UndefinedValue());
if (!JS_Stringify(rq.cx, obj, nullptr, indentVal, &Stringifier::callback, &str))
{
ScriptException::CatchPending(rq);
return std::string();
}
return str.stream.str();
}
std::string ScriptInterface::ToString(JS::MutableHandleValue obj, bool pretty) const
{
ScriptRequest rq(this);
if (obj.isUndefined())
return "(void 0)";
// Try to stringify as JSON if possible
// (TODO: this is maybe a bad idea since it'll drop 'undefined' values silently)
if (pretty)
{
Stringifier str;
JS::RootedValue indentVal(rq.cx, JS::Int32Value(2));
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
JS_ClearPendingException(rq.cx);
}
// Caller didn't want pretty output, or JSON conversion failed (e.g. due to cycles),
// so fall back to obj.toSource()
std::wstring source = L"(error)";
ScriptFunction::Call(rq, obj, "toSource", source);
return utf8_from_wstring(source);
}

View File

@ -18,8 +18,6 @@
#ifndef INCLUDED_SCRIPTINTERFACE
#define INCLUDED_SCRIPTINTERFACE
#include "lib/file/vfs/vfs_path.h"
#include "maths/Fixed.h"
#include "ps/Errors.h"
#include "scriptinterface/ScriptConversions.h"
#include "scriptinterface/ScriptExceptions.h"
@ -55,10 +53,13 @@ struct ScriptInterface_impl;
class ScriptContext;
// Using a global object for the context is a workaround until Simulation, AI, etc,
// use their own threads and also their own contexts.
extern thread_local shared_ptr<ScriptContext> g_ScriptContext;
extern thread_local std::shared_ptr<ScriptContext> g_ScriptContext;
namespace boost { namespace random { class rand48; } }
class Path;
using VfsPath = Path;
/**
* Abstraction around a SpiderMonkey JS::Realm.
*
@ -145,31 +146,6 @@ public:
bool SetPrototype(JS::HandleValue obj, JS::HandleValue proto);
/**
* Convert an object to a UTF-8 encoded string, either with JSON
* (if pretty == true and there is no JSON error) or with toSource().
*
* We have to use a mutable handle because JS_Stringify requires that for unknown reasons.
*/
std::string ToString(JS::MutableHandleValue obj, bool pretty = false) const;
/**
* Parse a UTF-8-encoded JSON string. Returns the unmodified value on error
* and prints an error message.
* @return true on success; false otherwise
*/
bool ParseJSON(const std::string& string_utf8, JS::MutableHandleValue out) const;
/**
* Read a JSON file. Returns the unmodified value on error and prints an error message.
*/
void ReadJSONFile(const VfsPath& path, JS::MutableHandleValue out) const;
/**
* Stringify to a JSON string, UTF-8 encoded. Returns an empty string on error.
*/
std::string StringifyJSON(JS::MutableHandleValue obj, bool indent = true) const;
/**
* Load and execute the given script in a new function scope.
* @param filename Name for debugging purposes (not used to load the file)

View File

@ -18,9 +18,10 @@
#include "lib/self_test.h"
#include "scriptinterface/FunctionWrapper.h"
#include "scriptinterface/JSON.h"
#include "scriptinterface/Object.h"
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/StructuredClone.h"
#include "scriptinterface/Object.h"
#include "ps/CLogger.h"
@ -239,11 +240,11 @@ public:
JS::RootedValue val(rq.cx);
TS_ASSERT(script.Eval(input.c_str(), &val));
std::string stringified = script.StringifyJSON(&val);
std::string stringified = Script::StringifyJSON(rq, &val);
TS_ASSERT_STR_EQUALS(stringified, "{\n \"x\": 1,\n \"z\": [\n 2,\n \"3\u263A\\ud800\"\n ],\n \"y\": true\n}");
TS_ASSERT(script.ParseJSON(stringified, &val));
TS_ASSERT_STR_EQUALS(script.ToString(&val), "({x:1, z:[2, \"3\\u263A\\uD800\"], y:true})");
TS_ASSERT(Script::ParseJSON(rq, stringified, &val));
TS_ASSERT_STR_EQUALS(Script::ToString(rq, &val), "({x:1, z:[2, \"3\\u263A\\uD800\"], y:true})");
}
// This function tests a common way to mod functions, by creating a wrapper that

View File

@ -22,6 +22,7 @@
#include "scriptinterface/FunctionWrapper.h"
#include "scriptinterface/ScriptContext.h"
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/JSON.h"
#include "scriptinterface/StructuredClone.h"
#include "simulation2/MessageTypes.h"
@ -813,7 +814,7 @@ void CSimulation2::GetInitAttributes(JS::MutableHandleValue ret)
void CSimulation2::SetMapSettings(const std::string& settings)
{
m->m_ComponentManager.GetScriptInterface().ParseJSON(settings, &m->m_MapSettings);
Script::ParseJSON(ScriptRequest(m->m_ComponentManager.GetScriptInterface()), settings, &m->m_MapSettings);
}
void CSimulation2::SetMapSettings(JS::HandleValue settings)
@ -826,7 +827,7 @@ void CSimulation2::SetMapSettings(JS::HandleValue settings)
std::string CSimulation2::GetMapSettingsString()
{
return m->m_ComponentManager.GetScriptInterface().StringifyJSON(&m->m_MapSettings);
return Script::StringifyJSON(ScriptRequest(m->m_ComponentManager.GetScriptInterface()), &m->m_MapSettings);
}
void CSimulation2::GetMapSettings(JS::MutableHandleValue ret)
@ -997,5 +998,5 @@ std::string CSimulation2::GetAIData()
if (!Script::CreateObject(rq, &ais, "AIData", aiData))
return std::string();
return scriptInterface.StringifyJSON(&ais);
return Script::StringifyJSON(rq, &ais);
}

View File

@ -35,6 +35,7 @@
#include "scriptinterface/FunctionWrapper.h"
#include "scriptinterface/ScriptContext.h"
#include "scriptinterface/StructuredClone.h"
#include "scriptinterface/JSON.h"
#include "simulation2/components/ICmpAIInterface.h"
#include "simulation2/components/ICmpCommandQueue.h"
#include "simulation2/components/ICmpObstructionManager.h"
@ -768,7 +769,7 @@ private:
if (m_PlayerMetadata.find(path) == m_PlayerMetadata.end())
{
// Load and cache the AI player metadata
m_ScriptInterface->ReadJSONFile(path, out);
Script::ReadJSONFile(ScriptRequest(m_ScriptInterface), path, out);
m_PlayerMetadata[path] = JS::Heap<JS::Value>(out);
return;
}

View File

@ -24,6 +24,7 @@
#include "ps/Game.h"
#include "ps/Profile.h"
#include "scriptinterface/FunctionWrapper.h"
#include "scriptinterface/JSON.h"
#include "simulation2/system/TurnManager.h"
class CCmpCommandQueue : public ICmpCommandQueue
@ -92,7 +93,7 @@ public:
JS::RootedValue cmd(rq.cx, cmd1.get());
PROFILE2_EVENT("post net command");
PROFILE2_ATTR("command: %s", GetSimContext().GetScriptInterface().StringifyJSON(&cmd, false).c_str());
PROFILE2_ATTR("command: %s", Script::StringifyJSON(rq, &cmd, false).c_str());
// TODO: would be nicer to not use globals
if (g_Game && g_Game->GetTurnManager())

View File

@ -21,6 +21,7 @@
#include "simulation2/system/InterfaceScripted.h"
#include "scriptinterface/ScriptExtraHeaders.h"
#include "scriptinterface/JSON.h"
#include "lib/file/vfs/vfs_util.h"
#include "ps/Filesystem.h"
@ -68,7 +69,7 @@ public:
Script::CreateObject(rq, &ai);
JS::RootedValue data(rq.cx);
self->m_ScriptInterface.ReadJSONFile(pathname, &data);
Script::ReadJSONFile(rq, pathname, &data);
Script::SetProperty(rq, ai, "id", dirname, true);
Script::SetProperty(rq, ai, "data", data, true);
u32 length;

View File

@ -25,7 +25,6 @@
#include "ps/GameSetup/Config.h"
#include "ps/Pyrogenesis.h"
#include "scriptinterface/FunctionWrapper.h"
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/StructuredClone.h"
#include "simulation2/components/ICmpAIManager.h"
#include "simulation2/components/ICmpCommandQueue.h"

View File

@ -17,7 +17,7 @@
#include "precompiled.h"
#include "ps/CLogger.h"
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/ScriptConversions.h"
#include "simulation2/MessageTypes.h"
#define TOJSVAL_SETUP() \
@ -52,8 +52,9 @@
JS::Value CMessage::ToJSValCached(const ScriptInterface& scriptInterface) const
{
ScriptRequest rq(scriptInterface);
if (!m_Cached)
m_Cached.reset(new JS::PersistentRootedValue(scriptInterface.GetGeneralJSContext(), ToJSVal(scriptInterface)));
m_Cached.reset(new JS::PersistentRootedValue(rq.cx, ToJSVal(scriptInterface)));
return m_Cached->get();
}

View File

@ -23,8 +23,10 @@
#include "ps/CLogger.h"
#include "scriptinterface/FunctionWrapper.h"
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/ScriptExtraHeaders.h"
#include "scriptinterface/ScriptRequest.h"
#include "scriptinterface/JSON.h"
#include "SerializedScriptTypes.h"
static u8 GetArrayType(js::Scalar::Type arrayType)
@ -59,13 +61,13 @@ CBinarySerializerScriptImpl::CBinarySerializerScriptImpl(const ScriptInterface&
m_ScriptInterface(scriptInterface), m_Serializer(serializer), m_ScriptBackrefsNext(0)
{
ScriptRequest rq(m_ScriptInterface);
JS_AddExtraGCRootsTracer(m_ScriptInterface.GetGeneralJSContext(), Trace, this);
JS_AddExtraGCRootsTracer(rq.cx, Trace, this);
}
CBinarySerializerScriptImpl::~CBinarySerializerScriptImpl()
{
JS_RemoveExtraGCRootsTracer(m_ScriptInterface.GetGeneralJSContext(), Trace, this);
ScriptRequest rq(m_ScriptInterface);
JS_RemoveExtraGCRootsTracer(rq.cx, Trace, this);
}
void CBinarySerializerScriptImpl::HandleScriptVal(JS::HandleValue val)
@ -480,7 +482,7 @@ u32 CBinarySerializerScriptImpl::GetScriptBackrefTag(JS::HandleObject obj)
if (!m_ScriptBackrefTags.put(JS::Heap<JSObject*>(obj.get()), ++m_ScriptBackrefsNext))
{
JS::RootedValue objval(rq.cx, JS::ObjectValue(*obj.get()));
LOGERROR("BinarySerializer: error at insertion. Object was %s", m_ScriptInterface.ToString(&objval));
LOGERROR("BinarySerializer: error at insertion. Object was %s", Script::ToString(rq, &objval));
return 0;
}
// Return 0 to mean "you have to serialize this object";

View File

@ -21,7 +21,8 @@
#include "scriptinterface/FunctionWrapper.h"
#include "scriptinterface/Object.h"
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/ScriptRequest.h"
#include "scriptinterface/JSON.h"
#include "lib/secure_crt.h"
#include "lib/utf8.h"
@ -159,12 +160,12 @@ void CDebugSerializer::PutScriptVal(const char* name, JS::MutableHandleValue val
// If the value has a Serialize property, pretty-parse that instead.
// (this gives more accurate OOS reports).
ScriptFunction::Call(rq, value, "Serialize", &serialize);
std::string serialized_source = m_ScriptInterface.ToString(&serialize, true);
std::string serialized_source = Script::ToString(rq, &serialize, true);
m_Stream << INDENT << name << ": " << serialized_source << "\n";
}
else
{
std::string source = m_ScriptInterface.ToString(value, true);
std::string source = Script::ToString(rq, value, true);
m_Stream << INDENT << name << ": " << source << "\n";
}
}

View File

@ -22,7 +22,9 @@
#include "gui/GUIManager.h"
#include "ps/CLogger.h"
#include "ps/Util.h"
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/ScriptConversions.h"
#include "scriptinterface/ScriptRequest.h"
#include "scriptinterface/JSON.h"
#include "simulation2/Simulation2.h"
const CStr CReplayTurnManager::EventNameReplayFinished = "ReplayFinished";
@ -117,7 +119,7 @@ void CReplayTurnManager::DoTurn(u32 turn)
for (const std::pair<player_id_t, std::string>& p : m_ReplayCommands[turn])
{
JS::RootedValue command(rq.cx);
m_Simulation2.GetScriptInterface().ParseJSON(p.second, &command);
Script::ParseJSON(rq, p.second, &command);
AddCommand(m_ClientId, p.first, command, m_CurrentTurn + 1);
}

View File

@ -27,8 +27,6 @@
#include "ps/Replay.h"
#include "ps/Util.h"
#include "scriptinterface/Object.h"
#include "scriptinterface/ScriptExtraHeaders.h" // StructuredClone
#include "scriptinterface/ScriptInterface.h"
#include "simulation2/Simulation2.h"
#if 0
@ -42,9 +40,10 @@ const CStr CTurnManager::EventNameSavegameLoaded = "SavegameLoaded";
CTurnManager::CTurnManager(CSimulation2& simulation, u32 defaultTurnLength, u32 commandDelay, int clientId, IReplayLogger& replay)
: m_Simulation2(simulation), m_CurrentTurn(0), m_CommandDelay(commandDelay), m_ReadyTurn(commandDelay - 1), m_TurnLength(defaultTurnLength),
m_PlayerId(-1), m_ClientId(clientId), m_DeltaSimTime(0), m_Replay(replay),
m_FinalTurn(std::numeric_limits<u32>::max()), m_TimeWarpNumTurns(0),
m_QuickSaveMetadata(m_Simulation2.GetScriptInterface().GetGeneralJSContext())
m_FinalTurn(std::numeric_limits<u32>::max()), m_TimeWarpNumTurns(0)
{
ScriptRequest rq(m_Simulation2.GetScriptInterface());
m_QuickSaveMetadata.init(rq.cx);
m_QueuedCommands.resize(1);
}

View File

@ -30,6 +30,8 @@
#include "ps/Filesystem.h"
#include "ps/CLogger.h"
#include "ps/XML/Xeromyces.h"
#include "scriptinterface/JSON.h"
#include "scriptinterface/ScriptRequest.h"
class TestCmpTemplateManager : public CxxTest::TestSuite
{
@ -106,19 +108,19 @@ public:
const CParamNode* inherit1 = tempMan->LoadTemplate(ent2, "inherit1");
JS::RootedValue val(rq.cx);
Script::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\"}})");
TS_ASSERT_STR_EQUALS(Script::ToString(rq, &val), "({Test1A:{'@a':\"a1\", '@b':\"b1\", '@c':\"c1\", d:\"d1\", e:\"e1\", f:\"f1\"}})");
const CParamNode* inherit2 = tempMan->LoadTemplate(ent2, "inherit2");
Script::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\"}})");
TS_ASSERT_STR_EQUALS(Script::ToString(rq, &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");
Script::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\"})");
TS_ASSERT_STR_EQUALS(Script::ToString(rq, &val), "({Actor:\"example1\", ActorOnly:(void 0), SilhouetteDisplay:\"false\", SilhouetteOccluder:\"false\", VisibleInAtlasOnly:\"false\"})");
const CParamNode* foundation = tempMan->LoadTemplate(ent2, "foundation|actor|example1");
Script::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\"})");
TS_ASSERT_STR_EQUALS(Script::ToString(rq, &val), "({Actor:\"example1\", ActorOnly:(void 0), Foundation:(void 0), SilhouetteDisplay:\"false\", SilhouetteOccluder:\"false\", VisibleInAtlasOnly:\"false\"})");
#define GET_FIRST_ELEMENT(n, templateName) \
const CParamNode* n = tempMan->LoadTemplate(ent2, templateName); \
@ -131,14 +133,14 @@ public:
}
GET_FIRST_ELEMENT(n1, "inherit_a");
TS_ASSERT_STR_EQUALS(man.GetScriptInterface().ToString(&val), "({'@datatype':\"tokens\", _string:\"a b c\"})");
TS_ASSERT_STR_EQUALS(Script::ToString(rq, &val), "({'@datatype':\"tokens\", _string:\"a b c\"})");
GET_FIRST_ELEMENT(n2, "inherit_b");
TS_ASSERT_STR_EQUALS(man.GetScriptInterface().ToString(&val), "({'@datatype':\"tokens\", _string:\"a b c d\"})");
TS_ASSERT_STR_EQUALS(Script::ToString(rq, &val), "({'@datatype':\"tokens\", _string:\"a b c d\"})");
GET_FIRST_ELEMENT(n3, "inherit_c");
TS_ASSERT_STR_EQUALS(man.GetScriptInterface().ToString(&val), "({'@a':\"b\", _string:\"a b c\"})");
TS_ASSERT_STR_EQUALS(Script::ToString(rq, &val), "({'@a':\"b\", _string:\"a b c\"})");
GET_FIRST_ELEMENT(n4, "inherit_d");
TS_ASSERT_STR_EQUALS(man.GetScriptInterface().ToString(&val), "({'@a':\"b\", '@c':\"d\"})");
TS_ASSERT_STR_EQUALS(Script::ToString(rq, &val), "({'@a':\"b\", '@c':\"d\"})");
#undef GET_FIRST_ELEMENT
}

View File

@ -46,7 +46,7 @@
#include "renderer/Scene.h"
#include "renderer/SkyManager.h"
#include "renderer/WaterManager.h"
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/ScriptContext.h"
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpAttack.h"
#include "simulation2/components/ICmpOwnership.h"

View File

@ -44,7 +44,7 @@
#include "renderer/Renderer.h"
#include "renderer/WaterManager.h"
#include "scriptinterface/Object.h"
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/JSON.h"
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpOwnership.h"
#include "simulation2/components/ICmpPlayer.h"
@ -108,7 +108,7 @@ QUERYHANDLER(GenerateMap)
ScriptRequest rq(scriptInterface);
JS::RootedValue settings(rq.cx);
scriptInterface.ParseJSON(*msg->settings, &settings);
Script::ParseJSON(rq, *msg->settings, &settings);
Script::SetProperty(rq, settings, "mapType", "random");
JS::RootedValue attrs(rq.cx);