SpiderMonkey 31 upgrade

This upgrade also introduces exact stack rooting (see to the wiki:
JSRootingGuide) and fixes problems with moving GC. This allows us to
enable generational garbage collection (GGC).
Measurements a few months ago have shown a performance improvement of a
non-visual replay of around 13.5%. This probably varies quite a bit, but
it should be somewhere between 5-20%. Memory usage has also been
improved. Check the forum thread for details.

Thanks to everyone from the team who helped with this directly or
indirectly (review, finding and fixing issues, the required C++11
upgrade, the new autobuilder etc.)! Also thanks to the SpiderMonkey
developers who helped on the #jsapi channel or elsewhere!

Fixes #2462, #2415, #2428, #2684, #1374
Refs #2973, #2669

This was SVN commit r16214.
This commit is contained in:
Yves 2015-01-24 14:46:52 +00:00
parent b9e46f386b
commit c02a7e1a7b
107 changed files with 1961 additions and 2065 deletions

View File

@ -51,6 +51,9 @@ in particular, let us know and we can try to clarify it.
/source/lib
MIT
/source/scriptinterface/third_party
MPL 2.0
/source/third_party/cppformat
BSD

View File

@ -155,7 +155,10 @@ function updateSubject(newSubject)
function updatePlayerList()
{
var playersBox = Engine.GetGUIObjectByName("playersBox");
[playerList, presenceList, nickList, ratingList] = [[],[],[],[]];
var playerList = [];
var presenceList = [];
var nickList = [];
var ratingList = [];
var cleanPlayerList = Engine.GetPlayerList();
// Sort the player list, ignoring case.
cleanPlayerList.sort(function(a,b)

View File

@ -625,14 +625,15 @@ extern_lib_defs = {
},
spidermonkey = {
compile_settings = function()
if _OPTIONS["with-system-mozjs24"] then
if _OPTIONS["with-system-mozjs31"] then
if not _OPTIONS["android"] then
pkgconfig_cflags("mozjs-24")
pkgconfig_cflags("mozjs-31")
end
defines { "WITH_SYSTEM_MOZJS24" }
defines { "WITH_SYSTEM_MOZJS31" }
else
if os.is("windows") then
include_dir = "include-win32"
buildoptions { "/FI\"js/RequiredDefines.h\"" }
else
include_dir = "include-unix"
end
@ -645,12 +646,12 @@ extern_lib_defs = {
end
end,
link_settings = function()
if _OPTIONS["with-system-mozjs24"] then
if _OPTIONS["with-system-mozjs31"] then
if _OPTIONS["android"] then
links { "mozjs-24" }
links { "mozjs-31" }
else
pkgconfig_libs("nspr")
pkgconfig_libs("mozjs-24")
pkgconfig_libs("mozjs-31")
end
else
if os.is("macosx") then
@ -658,9 +659,9 @@ extern_lib_defs = {
links { "nspr4", "plc4", "plds4" }
end
configuration "Debug"
links { "mozjs24-ps-debug" }
links { "mozjs31-ps-debug" }
configuration "Release"
links { "mozjs24-ps-release" }
links { "mozjs31-ps-release" }
configuration { }
add_source_lib_paths("spidermonkey")
end

View File

@ -8,7 +8,7 @@ newoption { trigger = "jenkins-tests", description = "Configure CxxTest to use t
newoption { trigger = "minimal-flags", description = "Only set compiler/linker flags that are really needed. Has no effect on Windows builds" }
newoption { trigger = "outpath", description = "Location for generated project files" }
newoption { trigger = "sdl2", description = "Experimental build using SDL 2" }
newoption { trigger = "with-system-mozjs24", description = "Search standard paths for libmozjs24, instead of using bundled copy" }
newoption { trigger = "with-system-mozjs31", description = "Search standard paths for libmozjs31, instead of using bundled copy" }
newoption { trigger = "with-system-nvtt", description = "Search standard paths for nvidia-texture-tools library, instead of using bundled copy" }
newoption { trigger = "without-audio", description = "Disable use of OpenAL/Ogg/Vorbis APIs" }
newoption { trigger = "without-lobby", description = "Disable the use of gloox and the multiplayer lobby" }
@ -683,6 +683,7 @@ function setup_all_libs ()
source_dirs = {
"scriptinterface",
"scriptinterface/third_party"
}
extern_libs = {
"boost",

View File

@ -24,6 +24,9 @@ if [ "`uname -s`" != "Darwin" ]; then
(cd ../../libraries/source/fcollada/src && rm -rf ./output)
(cd ../../libraries/source/spidermonkey && rm -f .already-built)
(cd ../../libraries/source/spidermonkey && rm -rf ./mozjs31)
# Still delete the directory of the previous SpiderMonkey version to
# avoid wasting disk space if people clean workspaces after updating.
(cd ../../libraries/source/spidermonkey && rm -rf ./mozjs24)
(cd ../../libraries/source/nvtt/src && rm -rf ./build)
fi

View File

@ -45,7 +45,7 @@ ENET_VERSION="enet-1.3.12"
MINIUPNPC_VERSION="miniupnpc-1.9"
# --------------------------------------------------------------
# Bundled with the game:
# * SpiderMonkey 24
# * SpiderMonkey 31
# * NVTT
# * FCollada
# --------------------------------------------------------------
@ -657,10 +657,9 @@ popd > /dev/null
# be customized, so we build and install them from bundled sources
# --------------------------------------------------------------------
echo -e "Building Spidermonkey..."
LIB_VERSION="mozjs-24.2.0"
LIB_ARCHIVE="$LIB_VERSION.tar.bz2"
LIB_DIRECTORY="mozjs24"
LIB_VERSION="mozjs-31.2.0"
LIB_ARCHIVE="$LIB_VERSION.rc0.tar.bz2"
LIB_DIRECTORY="mozjs31"
pushd ../source/spidermonkey/ > /dev/null
@ -679,10 +678,10 @@ then
pushd $LIB_DIRECTORY/js/src
# We want separate debug/release versions of the library, so change their install name in the Makefile
perl -i.bak -pe 's/(^STATIC_LIBRARY_NAME\s+=).*/$1mozjs24-ps-debug/' Makefile.in
perl -i.bak -pe 's/js_static/mozjs24-ps-debug/g' shell/Makefile.in
perl -i.bak -pe 's/(^STATIC_LIBRARY_NAME\s+=).*/$1mozjs31-ps-debug/' Makefile.in
perl -i.bak -pe 's/js_static/mozjs31-ps-debug/g' shell/Makefile.in
CONF_OPTS="--target=$ARCH-apple-darwin --prefix=${INSTALL_DIR} --with-system-nspr --with-nspr-prefix=${NSPR_DIR} --with-system-zlib=${ZLIB_DIR} --enable-threadsafe --disable-tests --disable-shared-js" # --enable-trace-logging"
CONF_OPTS="--target=$ARCH-apple-darwin --prefix=${INSTALL_DIR} --with-system-nspr --with-nspr-prefix=${NSPR_DIR} --with-system-zlib=${ZLIB_DIR} --enable-gcgenerational --disable-tests --disable-shared-js" # --enable-trace-logging"
# Uncomment this line for 32-bit 10.5 cross compile:
#CONF_OPTS="$CONF_OPTS --target=i386-apple-darwin9.0.0"
if [[ $MIN_OSX_VERSION && ${MIN_OSX_VERSION-_} ]]; then
@ -692,9 +691,6 @@ then
CONF_OPTS="$CONF_OPTS --with-macosx-sdk=$SYSROOT"
fi
# apply patch to fix clang build on Mavericks (see https://bugzilla.mozilla.org/show_bug.cgi?id=917526)
patch -p0 -d ../../ -i ../../../osx/patches/sm-mavericks-clang-fix.diff || die "Spidermonkey build failed"
mkdir -p build-debug
pushd build-debug
(CC="clang" CXX="clang++" AR=ar CROSS_COMPILE=1 ../configure $CONF_OPTS --enable-debug --disable-optimize --enable-js-diagnostics --enable-gczeal && make ${JOBS}) || die "Spidermonkey build failed"
@ -706,8 +702,8 @@ then
mv Makefile.in.bak Makefile.in
mv shell/Makefile.in.bak shell/Makefile.in
perl -i.bak -pe 's/(^STATIC_LIBRARY_NAME\s+=).*/$1mozjs24-ps-release/' Makefile.in
perl -i.bak -pe 's/js_static/mozjs24-ps-release/g' shell/Makefile.in
perl -i.bak -pe 's/(^STATIC_LIBRARY_NAME\s+=).*/$1mozjs31-ps-release/' Makefile.in
perl -i.bak -pe 's/js_static/mozjs31-ps-release/g' shell/Makefile.in
mkdir -p build-release
pushd build-release
(CC="clang" CXX="clang++" AR=ar CROSS_COMPILE=1 ../configure $CONF_OPTS --enable-optimize && make ${JOBS}) || die "Spidermonkey build failed"

View File

@ -1,16 +0,0 @@
--- js/src/configure 2013-12-11 14:23:20.000000000 -0800
+++ js/src/configure 2014-03-10 19:12:35.000000000 -0700
@@ -6229,7 +6229,12 @@
if test "$GNU_CC"; then
# Per bug 719659 comment 2, some of the headers on ancient build machines
# may require gnu89 inline semantics. But otherwise, we use C99.
- CFLAGS="$CFLAGS -std=gnu99 -fgnu89-inline"
+ # But on OS X we just use C99 plus GNU extensions, in order to fix
+ # bug 917526.
+ CFLAGS="$CFLAGS -std=gnu99"
+ if test "${OS_ARCH}" != Darwin; then
+ CFLAGS="$CFLAGS -fgnu89-inline"
+ fi
MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-h,$(notdir $@) -o $@'
MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-h,$(notdir $@) -o $@'
DSO_LDOPTS='-shared'

View File

@ -60,7 +60,7 @@ void* CMapGeneratorWorker::RunThread(void *data)
CMapGeneratorWorker* self = static_cast<CMapGeneratorWorker*>(data);
self->m_ScriptInterface = new ScriptInterface("RMS", "MapGenerator", ScriptInterface::CreateRuntime(RMS_RUNTIME_SIZE));
self->m_ScriptInterface = new ScriptInterface("RMS", "MapGenerator", ScriptInterface::CreateRuntime(g_ScriptRuntime, RMS_RUNTIME_SIZE));
// Run map generation scripts
if ((!self->Run()) || (self->m_Progress > 0))
@ -99,7 +99,7 @@ bool CMapGeneratorWorker::Run()
// Functions for RMS
m_ScriptInterface->RegisterFunction<bool, std::wstring, CMapGeneratorWorker::LoadLibrary>("LoadLibrary");
m_ScriptInterface->RegisterFunction<void, CScriptValRooted, CMapGeneratorWorker::ExportMap>("ExportMap");
m_ScriptInterface->RegisterFunction<void, JS::HandleValue, CMapGeneratorWorker::ExportMap>("ExportMap");
m_ScriptInterface->RegisterFunction<void, int, CMapGeneratorWorker::SetProgress>("SetProgress");
m_ScriptInterface->RegisterFunction<void, CMapGeneratorWorker::MaybeGC>("MaybeGC");
m_ScriptInterface->RegisterFunction<std::vector<std::string>, CMapGeneratorWorker::GetCivData>("GetCivData");
@ -162,14 +162,9 @@ bool CMapGeneratorWorker::LoadLibrary(ScriptInterface::CxPrivate* pCxPrivate, st
return self->LoadScripts(name);
}
void CMapGeneratorWorker::ExportMap(ScriptInterface::CxPrivate* pCxPrivate, CScriptValRooted data1)
void CMapGeneratorWorker::ExportMap(ScriptInterface::CxPrivate* pCxPrivate, JS::HandleValue data)
{
CMapGeneratorWorker* self = static_cast<CMapGeneratorWorker*>(pCxPrivate->pCBData);
JSContext* cx = self->m_ScriptInterface->GetContext();
JSAutoRequest rq(cx);
// TODO: Get Handle parameter directly with SpiderMonkey 31
JS::RootedValue data(cx, data1.get());
// Copy results
CScopeLock lock(self->m_WorkerMutex);

View File

@ -122,7 +122,7 @@ private:
// callbacks for script functions
static bool LoadLibrary(ScriptInterface::CxPrivate* pCxPrivate, std::wstring name);
static void ExportMap(ScriptInterface::CxPrivate* pCxPrivate, CScriptValRooted data);
static void ExportMap(ScriptInterface::CxPrivate* pCxPrivate, JS::HandleValue data);
static void SetProgress(ScriptInterface::CxPrivate* pCxPrivate, int progress);
static void MaybeGC(ScriptInterface::CxPrivate* pCxPrivate);
static std::vector<std::string> GetCivData(ScriptInterface::CxPrivate* pCxPrivate);

View File

@ -64,7 +64,7 @@ CMapReader::CMapReader()
}
// LoadMap: try to load the map from given file; reinitialise the scene to new data if successful
void CMapReader::LoadMap(const VfsPath& pathname, const CScriptValRooted& settings, CTerrain *pTerrain_,
void CMapReader::LoadMap(const VfsPath& pathname, JSRuntime* rt, 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)
@ -83,7 +83,7 @@ void CMapReader::LoadMap(const VfsPath& pathname, const CScriptValRooted& setti
m_PlayerID = playerID_;
m_SkipEntities = skipEntities;
m_StartingCameraTarget = INVALID_ENTITY;
m_ScriptSettings = settings;
m_ScriptSettings.set(rt, settings);
filename_xml = pathname.ChangeExtension(L".xml");
@ -118,7 +118,7 @@ void CMapReader::LoadMap(const VfsPath& pathname, const CScriptValRooted& setti
pPostproc->SetPostEffect(L"default");
// load map or script settings script
if (settings.undefined())
if (settings.isUndefined())
RegMemFun(this, &CMapReader::LoadScriptSettings, L"CMapReader::LoadScriptSettings", 50);
else
RegMemFun(this, &CMapReader::LoadRMSettings, L"CMapReader::LoadRMSettings", 50);
@ -149,14 +149,16 @@ void CMapReader::LoadMap(const VfsPath& pathname, const CScriptValRooted& setti
}
// LoadRandomMap: try to load the map data; reinitialise the scene to new data if successful
void CMapReader::LoadRandomMap(const CStrW& scriptFile, const CScriptValRooted& settings, CTerrain *pTerrain_,
void CMapReader::LoadRandomMap(const CStrW& scriptFile, JSRuntime* rt, JS::HandleValue settings, CTerrain *pTerrain_,
WaterManager* pWaterMan_, SkyManager* pSkyMan_,
CLightEnv *pLightEnv_, CGameView *pGameView_, CCinemaManager* pCinema_, CTriggerManager* pTrigMan_, CPostprocManager* pPostproc_,
CSimulation2 *pSimulation2_, int playerID_)
{
// latch parameters (held until DelayedLoadFinished)
m_ScriptFile = scriptFile;
m_ScriptSettings = settings;
pSimulation2 = pSimulation2_;
pSimContext = pSimulation2 ? &pSimulation2->GetSimContext() : NULL;
m_ScriptSettings.set(rt, settings);
pTerrain = pTerrain_;
pLightEnv = pLightEnv_;
pGameView = pGameView_;
@ -165,8 +167,6 @@ void CMapReader::LoadRandomMap(const CStrW& scriptFile, const CScriptValRooted&
pCinema = pCinema_;
pTrigMan = pTrigMan_;
pPostproc = pPostproc_;
pSimulation2 = pSimulation2_;
pSimContext = pSimulation2 ? &pSimulation2->GetSimContext() : NULL;
m_PlayerID = playerID_;
m_SkipEntities = false;
m_StartingCameraTarget = INVALID_ENTITY;
@ -384,20 +384,18 @@ PSRETURN CMapSummaryReader::LoadMap(const VfsPath& pathname)
return PSRETURN_OK;
}
CScriptValRooted CMapSummaryReader::GetMapSettings(ScriptInterface& scriptInterface)
void CMapSummaryReader::GetMapSettings(ScriptInterface& scriptInterface, JS::MutableHandleValue ret)
{
JSContext* cx = scriptInterface.GetContext();
JSAutoRequest rq(cx);
JS::RootedValue data(cx);
scriptInterface.Eval("({})", &data);
if (!m_ScriptSettings.empty())
{
JS::RootedValue scriptSettingsVal(cx);
scriptInterface.ParseJSON(m_ScriptSettings, &scriptSettingsVal);
scriptInterface.SetProperty(data, "settings", scriptSettingsVal, false);
}
return CScriptValRooted(cx, data);
scriptInterface.Eval("({})", ret);
if (m_ScriptSettings.empty())
return;
JS::RootedValue scriptSettingsVal(cx);
scriptInterface.ParseJSON(m_ScriptSettings, &scriptSettingsVal);
scriptInterface.SetProperty(ret, "settings", scriptSettingsVal, false);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -1253,7 +1251,7 @@ int CMapReader::LoadRMSettings()
{
// copy random map settings over to sim
ENSURE(pSimulation2);
pSimulation2->SetMapSettings(m_ScriptSettings);
pSimulation2->SetMapSettings(m_ScriptSettings.get());
return 0;
}
@ -1273,10 +1271,8 @@ int CMapReader::GenerateMap()
if (m_ScriptFile.length())
scriptPath = L"maps/random/"+m_ScriptFile;
// TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
JS::RootedValue tmpScriptSettings(cx, m_ScriptSettings.get());
// Stringify settings to pass across threads
std::string scriptSettings = pSimulation2->GetScriptInterface().StringifyJSON(&tmpScriptSettings);
std::string scriptSettings = pSimulation2->GetScriptInterface().StringifyJSON(&m_ScriptSettings.get());
// Try to generate map
m_MapGen->GenerateMap(scriptPath, scriptSettings);
@ -1305,7 +1301,7 @@ int CMapReader::GenerateMap()
}
else
{
m_MapData = CScriptValRooted(cx, data);
m_MapData.set(cx, data);
}
}
else
@ -1335,18 +1331,17 @@ int CMapReader::ParseTerrain()
{ LOGERROR("CMapReader::ParseTerrain() failed to get '%s' property", #prop);\
throw PSERROR_Game_World_MapLoadFailed("Error parsing terrain data.\nCheck application log for details"); }
JS::RootedValue tmpMapData(cx, m_MapData.get()); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
u32 size;
GET_TERRAIN_PROPERTY(tmpMapData, size, size)
GET_TERRAIN_PROPERTY(m_MapData.get(), size, size)
m_PatchesPerSide = size / PATCH_SIZE;
// flat heightmap of u16 data
GET_TERRAIN_PROPERTY(tmpMapData, height, m_Heightmap)
GET_TERRAIN_PROPERTY(m_MapData.get(), height, m_Heightmap)
// load textures
std::vector<std::string> textureNames;
GET_TERRAIN_PROPERTY(tmpMapData, textureNames, textureNames)
GET_TERRAIN_PROPERTY(m_MapData.get(), textureNames, textureNames)
num_terrain_tex = textureNames.size();
while (cur_terrain_tex < num_terrain_tex)
@ -1362,7 +1357,7 @@ int CMapReader::ParseTerrain()
m_Tiles.resize(SQR(size));
JS::RootedValue tileData(cx);
GET_TERRAIN_PROPERTY(tmpMapData, tileData, &tileData)
GET_TERRAIN_PROPERTY(m_MapData.get(), tileData, &tileData)
// parse tile data object into flat arrays
std::vector<u16> tileIndex;
@ -1404,13 +1399,11 @@ int CMapReader::ParseEntities()
TIMER(L"ParseEntities");
JSContext* cx = pSimulation2->GetScriptInterface().GetContext();
JSAutoRequest rq(cx);
JS::RootedValue tmpMapData(cx, m_MapData.get()); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
// parse entities from map data
std::vector<Entity> entities;
if (!pSimulation2->GetScriptInterface().GetProperty(tmpMapData, "entities", entities))
if (!pSimulation2->GetScriptInterface().GetProperty(m_MapData.get(), "entities", entities))
LOGWARNING("CMapReader::ParseEntities() failed to get 'entities' property");
CSimulation2& sim = *pSimulation2;
@ -1471,15 +1464,13 @@ int CMapReader::ParseEnvironment()
// parse environment settings from map data
JSContext* cx = pSimulation2->GetScriptInterface().GetContext();
JSAutoRequest rq(cx);
JS::RootedValue tmpMapData(cx, m_MapData.get()); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
#define GET_ENVIRONMENT_PROPERTY(val, prop, out)\
if (!pSimulation2->GetScriptInterface().GetProperty(val, #prop, out))\
LOGWARNING("CMapReader::ParseEnvironment() failed to get '%s' property", #prop);
JS::RootedValue envObj(cx);
GET_ENVIRONMENT_PROPERTY(tmpMapData, Environment, &envObj)
GET_ENVIRONMENT_PROPERTY(m_MapData.get(), Environment, &envObj)
if (envObj.isUndefined())
{
@ -1583,9 +1574,8 @@ int CMapReader::ParseCamera()
if (!pSimulation2->GetScriptInterface().GetProperty(val, #prop, out))\
LOGWARNING("CMapReader::ParseCamera() failed to get '%s' property", #prop);
JS::RootedValue tmpMapData(cx, m_MapData.get()); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
JS::RootedValue cameraObj(cx);
GET_CAMERA_PROPERTY(tmpMapData, Camera, &cameraObj)
GET_CAMERA_PROPERTY(m_MapData.get(), Camera, &cameraObj)
if (!cameraObj.isUndefined())
{ // If camera property exists, read values

View File

@ -37,7 +37,6 @@ class CTriggerManager;
class CSimulation2;
class CSimContext;
class CTerrainTextureEntry;
class CScriptValRooted;
class ScriptInterface;
class CGameView;
class CXMLReader;
@ -53,11 +52,11 @@ public:
~CMapReader();
// LoadMap: try to load the map from given file; reinitialise the scene to new data if successful
void LoadMap(const VfsPath& pathname, const CScriptValRooted& settings, CTerrain*, WaterManager*, SkyManager*, CLightEnv*, CGameView*,
void LoadMap(const VfsPath& pathname, JSRuntime* rt, JS::HandleValue settings, CTerrain*, WaterManager*, SkyManager*, CLightEnv*, CGameView*,
CCinemaManager*, CTriggerManager*, CPostprocManager* pPostproc, CSimulation2*, const CSimContext*,
int playerID, bool skipEntities);
void LoadRandomMap(const CStrW& scriptFile, const CScriptValRooted& settings, CTerrain*, WaterManager*, SkyManager*, CLightEnv*, CGameView*, CCinemaManager*, CTriggerManager*, CPostprocManager* pPostproc_, CSimulation2*, int playerID);
void LoadRandomMap(const CStrW& scriptFile, JSRuntime* rt, JS::HandleValue settings, CTerrain*, WaterManager*, SkyManager*, CLightEnv*, CGameView*, CCinemaManager*, CTriggerManager*, CPostprocManager* pPostproc_, CSimulation2*, int playerID);
private:
// Load script settings for use by scripts
@ -124,8 +123,8 @@ private:
// random map data
CStrW m_ScriptFile;
CScriptValRooted m_ScriptSettings;
CScriptValRooted m_MapData;
DefPersistentRooted<JS::Value> m_ScriptSettings;
DefPersistentRooted<JS::Value> m_MapData;
CMapGenerator* m_MapGen;
@ -177,7 +176,7 @@ public:
* }
* @endcode
*/
CScriptValRooted GetMapSettings(ScriptInterface& scriptInterface);
void GetMapSettings(ScriptInterface& scriptInterface, JS::MutableHandleValue);
private:
CStr m_ScriptSettings;

View File

@ -112,7 +112,6 @@ void CGUIManager::PopPageCB(shared_ptr<ScriptInterface::StructuredClone> args)
shared_ptr<ScriptInterface> scriptInterface = m_PageStack.back().gui->GetScriptInterface();
JSContext* cx = scriptInterface->GetContext();
JS::RootedValue initDataVal(cx);
if (initDataClone)
scriptInterface->ReadStructuredClone(initDataClone, &initDataVal);
else

View File

@ -83,11 +83,21 @@ IGUIObject::~IGUIObject()
}
}
}
if (m_pGUI)
JS_RemoveExtraGCRootsTracer(m_pGUI->GetScriptInterface()->GetJSRuntime(), Trace, this);
}
//-------------------------------------------------------------------
// Functions
//-------------------------------------------------------------------
void IGUIObject::SetGUI(CGUI * const &pGUI)
{
if (!m_pGUI)
JS_AddExtraGCRootsTracer(pGUI->GetScriptInterface()->GetJSRuntime(), Trace, this);
m_pGUI = pGUI;
}
void IGUIObject::AddChild(IGUIObject *pChild)
{
//
@ -435,8 +445,12 @@ void IGUIObject::RegisterScriptHandler(const CStr& Action, const CStr& Code, CGU
char buf[64];
sprintf_s(buf, ARRAY_SIZE(buf), "__eventhandler%d (%s)", x++, Action.c_str());
JS::CompileOptions options(cx);
options.setFileAndLine(CodeName.c_str(), 0);
options.setCompileAndGo(true);
JS::RootedFunction func(cx, JS_CompileFunction(cx, globalObj,
buf, paramCount, paramNames, Code.c_str(), Code.length(), CodeName.c_str(), 0));
buf, paramCount, paramNames, Code.c_str(), Code.length(), options));
if (!func)
return; // JS will report an error message
@ -447,7 +461,10 @@ void IGUIObject::RegisterScriptHandler(const CStr& Action, const CStr& Code, CGU
void IGUIObject::SetScriptHandler(const CStr& Action, JS::HandleObject Function)
{
m_ScriptHandlers[Action] = CScriptValRooted(m_pGUI->GetScriptInterface()->GetContext(), JS::ObjectValue(*Function));
// m_ScriptHandlers is only rooted after SetGUI() has been called (which sets up the GC trace callbacks),
// so we can't safely store objects in it if the GUI hasn't been set yet.
ENSURE(m_pGUI && "A GUI must be associated with the GUIObject before adding ScriptHandlers!");
m_ScriptHandlers[Action] = JS::Heap<JSObject*>(Function);
}
InReaction IGUIObject::SendEvent(EGUIMessageType type, const CStr& EventName)
@ -466,7 +483,7 @@ InReaction IGUIObject::SendEvent(EGUIMessageType type, const CStr& EventName)
void IGUIObject::ScriptEvent(const CStr& Action)
{
std::map<CStr, CScriptValRooted>::iterator it = m_ScriptHandlers.find(Action);
auto it = m_ScriptHandlers.find(Action);
if (it == m_ScriptHandlers.end())
return;
@ -483,9 +500,9 @@ void IGUIObject::ScriptEvent(const CStr& Action)
JS::AutoValueVector paramData(cx);
paramData.append(mouse);
JS::RootedObject obj(cx, GetJSObject());
JS::RootedValue handlerVal(cx, (*it).second.get());
JS::RootedValue handlerVal(cx, JS::ObjectValue(*it->second));
JS::RootedValue result(cx);
bool ok = JS_CallFunctionValue(cx, obj, handlerVal, paramData.length(), paramData.begin(), result.address());
bool ok = JS_CallFunctionValue(cx, obj, handlerVal, paramData, &result);
if (!ok)
{
// We have no way to propagate the script exception, so just ignore it
@ -495,20 +512,18 @@ void IGUIObject::ScriptEvent(const CStr& Action)
void IGUIObject::ScriptEvent(const CStr& Action, JS::HandleValue Argument)
{
std::map<CStr, CScriptValRooted>::iterator it = m_ScriptHandlers.find(Action);
auto it = m_ScriptHandlers.find(Action);
if (it == m_ScriptHandlers.end())
return;
JSContext* cx = m_pGUI->GetScriptInterface()->GetContext();
JSAutoRequest rq(cx);
JS::AutoValueVector paramData(cx);
paramData.append(Argument.get());
JS::RootedObject obj(cx, GetJSObject());
// TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
JS::RootedValue tmpScriptHandler(cx, (*it).second.get());
JS::RootedValue handlerVal(cx, JS::ObjectValue(*it->second));
JS::RootedValue result(cx);
bool ok = JS_CallFunctionValue(cx, obj, tmpScriptHandler, paramData.length(), paramData.begin(), result.address());
bool ok = JS_CallFunctionValue(cx, obj, handlerVal, paramData, &result);
if (!ok)
{
JS_ReportError(cx, "Errors executing script action \"%s\"", Action.c_str());
@ -522,13 +537,12 @@ JSObject* IGUIObject::GetJSObject()
// Cache the object when somebody first asks for it, because otherwise
// we end up doing far too much object allocation. TODO: Would be nice to
// not have these objects hang around forever using up memory, though.
if (m_JSObject.uninitialised())
if (m_JSObject.uninitialized())
{
JS::RootedObject obj(cx, m_pGUI->GetScriptInterface()->CreateCustomObject("GUIObject"));
m_JSObject = CScriptValRooted(cx, JS::ObjectValue(*obj));
JS_SetPrivate(obj, this);
m_JSObject.set(cx, m_pGUI->GetScriptInterface()->CreateCustomObject("GUIObject"));
JS_SetPrivate(m_JSObject.get(), this);
}
return &m_JSObject.get().toObject();
return m_JSObject.get();
}
CStr IGUIObject::GetPresentableName() const
@ -559,6 +573,12 @@ bool IGUIObject::IsRootObject() const
return (GetGUI() != 0 && m_pParent == GetGUI()->m_BaseObject);
}
void IGUIObject::TraceMember(JSTracer *trc)
{
for (auto& handler : m_ScriptHandlers)
JS_CallHeapObjectTracer(trc, &handler.second, "IGUIObject::m_ScriptHandlers");
}
PSRETURN IGUIObject::LogInvalidSettings(const CStr8 &Setting) const
{
LOGWARNING("IGUIObject: setting %s was not found on an object",

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2012 Wildfire Games.
/* Copyright (C) 2015 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -59,7 +59,6 @@ The base class of an object
struct SGUISetting;
struct SGUIStyle;
class CGUI;
class CScriptValRooted;
//--------------------------------------------------------
// Macros
@ -142,9 +141,9 @@ class IGUIObject
friend class GUITooltip;
// Allow getProperty to access things like GetParent()
friend JSBool JSI_IGUIObject::getProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::MutableHandleValue vp);
friend JSBool JSI_IGUIObject::setProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JSBool UNUSED(strict), JS::MutableHandleValue vp);
friend JSBool JSI_IGUIObject::getComputedSize(JSContext* cx, uint argc, jsval* vp);
friend bool JSI_IGUIObject::getProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::MutableHandleValue vp);
friend bool JSI_IGUIObject::setProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool UNUSED(strict), JS::MutableHandleValue vp);
friend bool JSI_IGUIObject::getComputedSize(JSContext* cx, uint argc, jsval* vp);
public:
IGUIObject();
@ -365,7 +364,7 @@ protected:
*/
virtual float GetBufferedZ() const;
void SetGUI(CGUI * const &pGUI) { m_pGUI = pGUI; }
void SetGUI(CGUI * const &pGUI);
/**
* Set parent of this object
@ -502,6 +501,13 @@ private:
*/
PSRETURN LogInvalidSettings(const CStr8& Setting) const;
static void Trace(JSTracer *trc, void *data)
{
reinterpret_cast<IGUIObject*>(data)->TraceMember(trc);
}
void TraceMember(JSTracer *trc);
// Variables
protected:
@ -551,10 +557,10 @@ private:
CGUI *m_pGUI;
// Internal storage for registered script handlers.
std::map<CStr, CScriptValRooted> m_ScriptHandlers;
std::map<CStr, JS::Heap<JSObject*> > m_ScriptHandlers;
// Cached JSObject representing this GUI object
CScriptValRooted m_JSObject;
DefPersistentRooted<JSObject*> m_JSObject;
};

View File

@ -23,7 +23,7 @@
#include "lib/external_libraries/libsdl.h"
#include "ps/Hotkey.h"
#define SET(obj, name, value) STMT(JS::RootedValue v_(cx); ToJSVal(cx, &v_, (value)); JS_SetProperty(cx, (obj), (name), v_.address()))
#define SET(obj, name, value) STMT(JS::RootedValue v_(cx); AssignOrToJSVal(cx, &v_, (value)); JS_SetProperty(cx, obj, (name), v_))
// ignore JS_SetProperty return value, because errors should be impossible
// and we can't do anything useful in the case of errors anyway
@ -52,7 +52,7 @@ template<> void ScriptInterface::ToJSVal<SDL_Event_>(JSContext* cx, JS::MutableH
default: typeName = "(unknown)"; break;
}
JS::RootedObject obj(cx, JS_NewObject(cx, NULL, NULL, NULL));
JS::RootedObject obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
if (!obj)
{
ret.setUndefined();
@ -77,14 +77,14 @@ template<> void ScriptInterface::ToJSVal<SDL_Event_>(JSContext* cx, JS::MutableH
// SET(obj, "which", (int)val.ev.key.which); // (not in wsdl.h)
// SET(obj, "state", (int)val.ev.key.state); // (not in wsdl.h)
JSObject* keysym = JS_NewObject(cx, NULL, NULL, NULL);
JS::RootedObject keysym(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
if (! keysym)
{
ret.setUndefined();
return;
}
JS::RootedValue keysymVal(cx, JS::ObjectValue(*keysym));
JS_SetProperty(cx, obj, "keysym", keysymVal.address());
JS_SetProperty(cx, obj, "keysym", keysymVal);
// SET(keysym, "scancode", (int)val.ev.key.keysym.scancode); // (not in wsdl.h)
SET(keysym, "sym", (int)val.ev.key.keysym.sym);
@ -98,7 +98,7 @@ template<> void ScriptInterface::ToJSVal<SDL_Event_>(JSContext* cx, JS::MutableH
else
#endif
{
SET(keysym, "unicode", CScriptVal(JSVAL_VOID));
SET(keysym, "unicode", JS::UndefinedHandleValue);
}
// TODO: scripts have no idea what all the key/mod enum values are;
// we should probably expose them as constants if we expect scripts to use them

View File

@ -27,20 +27,7 @@ JSClass JSI_GUISize::JSI_class = {
JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, JS_ResolveStub,
JS_ConvertStub, NULL,
NULL, NULL, NULL, JSI_GUISize::construct
};
JSPropertySpec JSI_GUISize::JSI_props[] =
{
{ "left", 0, JSPROP_ENUMERATE},
{ "top", 1, JSPROP_ENUMERATE},
{ "right", 2, JSPROP_ENUMERATE},
{ "bottom", 3, JSPROP_ENUMERATE},
{ "rleft", 4, JSPROP_ENUMERATE},
{ "rtop", 5, JSPROP_ENUMERATE},
{ "rright", 6, JSPROP_ENUMERATE},
{ "rbottom", 7, JSPROP_ENUMERATE},
{ 0 }
NULL, NULL, JSI_GUISize::construct, NULL
};
JSFunctionSpec JSI_GUISize::JSI_methods[] =
@ -49,52 +36,51 @@ JSFunctionSpec JSI_GUISize::JSI_methods[] =
JS_FS_END
};
JSBool JSI_GUISize::construct(JSContext* cx, uint argc, jsval* vp)
bool JSI_GUISize::construct(JSContext* cx, uint argc, jsval* vp)
{
JSAutoRequest rq(cx);
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
ScriptInterface* pScriptInterface = ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface;
JS::RootedObject obj(cx, pScriptInterface->CreateCustomObject("GUISize"));
if (args.length() == 8)
{
JS_SetProperty(cx, obj, "left", &args[0]);
JS_SetProperty(cx, obj, "top", &args[1]);
JS_SetProperty(cx, obj, "right", &args[2]);
JS_SetProperty(cx, obj, "bottom", &args[3]);
JS_SetProperty(cx, obj, "rleft", &args[4]);
JS_SetProperty(cx, obj, "rtop", &args[5]);
JS_SetProperty(cx, obj, "rright", &args[6]);
JS_SetProperty(cx, obj, "rbottom", &args[7]);
JS_SetProperty(cx, obj, "left", args[0]);
JS_SetProperty(cx, obj, "top", args[1]);
JS_SetProperty(cx, obj, "right", args[2]);
JS_SetProperty(cx, obj, "bottom", args[3]);
JS_SetProperty(cx, obj, "rleft", args[4]);
JS_SetProperty(cx, obj, "rtop", args[5]);
JS_SetProperty(cx, obj, "rright", args[6]);
JS_SetProperty(cx, obj, "rbottom", args[7]);
}
else if (args.length() == 4)
{
JS::RootedValue zero(cx, JSVAL_ZERO);
JS_SetProperty(cx, obj, "left", &args[0]);
JS_SetProperty(cx, obj, "top", &args[1]);
JS_SetProperty(cx, obj, "right", &args[2]);
JS_SetProperty(cx, obj, "bottom", &args[3]);
JS_SetProperty(cx, obj, "rleft", zero.address());
JS_SetProperty(cx, obj, "rtop", zero.address());
JS_SetProperty(cx, obj, "rright", zero.address());
JS_SetProperty(cx, obj, "rbottom", zero.address());
JS_SetProperty(cx, obj, "left", args[0]);
JS_SetProperty(cx, obj, "top", args[1]);
JS_SetProperty(cx, obj, "right", args[2]);
JS_SetProperty(cx, obj, "bottom", args[3]);
JS_SetProperty(cx, obj, "rleft", zero);
JS_SetProperty(cx, obj, "rtop", zero);
JS_SetProperty(cx, obj, "rright", zero);
JS_SetProperty(cx, obj, "rbottom", zero);
}
else
{
JS::RootedValue zero(cx, JSVAL_ZERO);
JS_SetProperty(cx, obj, "left", zero.address());
JS_SetProperty(cx, obj, "top", zero.address());
JS_SetProperty(cx, obj, "right", zero.address());
JS_SetProperty(cx, obj, "bottom", zero.address());
JS_SetProperty(cx, obj, "rleft", zero.address());
JS_SetProperty(cx, obj, "rtop", zero.address());
JS_SetProperty(cx, obj, "rright", zero.address());
JS_SetProperty(cx, obj, "rbottom", zero.address());
JS_SetProperty(cx, obj, "left", zero);
JS_SetProperty(cx, obj, "top", zero);
JS_SetProperty(cx, obj, "right", zero);
JS_SetProperty(cx, obj, "bottom", zero);
JS_SetProperty(cx, obj, "rleft", zero);
JS_SetProperty(cx, obj, "rtop", zero);
JS_SetProperty(cx, obj, "rright", zero);
JS_SetProperty(cx, obj, "rbottom", zero);
}
args.rval().setObject(*obj);
return JS_TRUE;
return true;
}
// Produces "10", "-10", "50%", "50%-10", "50%+10", etc
@ -106,7 +92,7 @@ CStr ToPercentString(double pix, double per)
return CStr::FromDouble(per)+"%"+( pix == 0.0 ? CStr() : pix > 0.0 ? CStr("+")+CStr::FromDouble(pix) : CStr::FromDouble(pix) );
}
JSBool JSI_GUISize::toString(JSContext* cx, uint argc, jsval* vp)
bool JSI_GUISize::toString(JSContext* cx, uint argc, jsval* vp)
{
UNUSED2(argc);
JS::CallReceiver rec = JS::CallReceiverFromVp(vp);
@ -132,11 +118,11 @@ JSBool JSI_GUISize::toString(JSContext* cx, uint argc, jsval* vp)
catch (PSERROR_Scripting_ConversionFailed&)
{
rec.rval().setString(JS_NewStringCopyZ(cx, "<Error converting value to numbers>"));
return JS_TRUE;
return true;
}
rec.rval().setString(JS_NewStringCopyZ(cx, buffer.c_str()));
return JS_TRUE;
return true;
}
@ -149,16 +135,7 @@ JSClass JSI_GUIColor::JSI_class = {
JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, JS_ResolveStub,
JS_ConvertStub, NULL,
NULL, NULL, NULL, JSI_GUIColor::construct
};
JSPropertySpec JSI_GUIColor::JSI_props[] =
{
{ "r", 0, JSPROP_ENUMERATE},
{ "g", 1, JSPROP_ENUMERATE},
{ "b", 2, JSPROP_ENUMERATE},
{ "a", 3, JSPROP_ENUMERATE},
{ 0 }
NULL, NULL, JSI_GUIColor::construct, NULL
};
JSFunctionSpec JSI_GUIColor::JSI_methods[] =
@ -167,7 +144,7 @@ JSFunctionSpec JSI_GUIColor::JSI_methods[] =
JS_FS_END
};
JSBool JSI_GUIColor::construct(JSContext* cx, uint argc, jsval* vp)
bool JSI_GUIColor::construct(JSContext* cx, uint argc, jsval* vp)
{
JSAutoRequest rq(cx);
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
@ -177,27 +154,27 @@ JSBool JSI_GUIColor::construct(JSContext* cx, uint argc, jsval* vp)
if (args.length() == 4)
{
JS_SetProperty(cx, obj, "r", &args[0]);
JS_SetProperty(cx, obj, "g", &args[1]);
JS_SetProperty(cx, obj, "b", &args[2]);
JS_SetProperty(cx, obj, "a", &args[3]);
JS_SetProperty(cx, obj, "r", args[0]);
JS_SetProperty(cx, obj, "g", args[1]);
JS_SetProperty(cx, obj, "b", args[2]);
JS_SetProperty(cx, obj, "a", args[3]);
}
else
{
// Nice magenta:
JS::RootedValue c(cx, JS::NumberValue(1.0));
JS_SetProperty(cx, obj, "r", c.address());
JS_SetProperty(cx, obj, "b", c.address());
JS_SetProperty(cx, obj, "a", c.address());
JS_SetProperty(cx, obj, "r", c);
JS_SetProperty(cx, obj, "b", c);
JS_SetProperty(cx, obj, "a", c);
c = JS::NumberValue(0.0);
JS_SetProperty(cx, obj, "g", c.address());
JS_SetProperty(cx, obj, "g", c);
}
args.rval().setObject(*obj);
return JS_TRUE;
return true;
}
JSBool JSI_GUIColor::toString(JSContext* cx, uint argc, jsval* vp)
bool JSI_GUIColor::toString(JSContext* cx, uint argc, jsval* vp)
{
UNUSED2(argc);
JS::CallReceiver rec = JS::CallReceiverFromVp(vp);
@ -216,7 +193,7 @@ JSBool JSI_GUIColor::toString(JSContext* cx, uint argc, jsval* vp)
(int)(255.0 * b),
(int)(255.0 * a));
rec.rval().setString(JS_NewStringCopyZ(cx, buffer));
return JS_TRUE;
return true;
}
/**** GUIMouse ****/
@ -228,15 +205,7 @@ JSClass JSI_GUIMouse::JSI_class = {
JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, JS_ResolveStub,
JS_ConvertStub, NULL,
NULL, NULL, NULL, JSI_GUIMouse::construct
};
JSPropertySpec JSI_GUIMouse::JSI_props[] =
{
{ "x", 0, JSPROP_ENUMERATE},
{ "y", 1, JSPROP_ENUMERATE},
{ "buttons", 2, JSPROP_ENUMERATE},
{ 0 }
NULL, NULL, JSI_GUIMouse::construct, NULL
};
JSFunctionSpec JSI_GUIMouse::JSI_methods[] =
@ -245,7 +214,7 @@ JSFunctionSpec JSI_GUIMouse::JSI_methods[] =
JS_FS_END
};
JSBool JSI_GUIMouse::construct(JSContext* cx, uint argc, jsval* vp)
bool JSI_GUIMouse::construct(JSContext* cx, uint argc, jsval* vp)
{
JSAutoRequest rq(cx);
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
@ -255,23 +224,23 @@ JSBool JSI_GUIMouse::construct(JSContext* cx, uint argc, jsval* vp)
if (args.length() == 3)
{
JS_SetProperty(cx, obj, "x", &args[0]);
JS_SetProperty(cx, obj, "y", &args[1]);
JS_SetProperty(cx, obj, "buttons", &args[2]);
JS_SetProperty(cx, obj, "x", args[0]);
JS_SetProperty(cx, obj, "y", args[1]);
JS_SetProperty(cx, obj, "buttons", args[2]);
}
else
{
JS::RootedValue zero (cx, JS::NumberValue(0));
JS_SetProperty(cx, obj, "x", zero.address());
JS_SetProperty(cx, obj, "y", zero.address());
JS_SetProperty(cx, obj, "buttons", zero.address());
JS_SetProperty(cx, obj, "x", zero);
JS_SetProperty(cx, obj, "y", zero);
JS_SetProperty(cx, obj, "buttons", zero);
}
args.rval().setObject(*obj);
return JS_TRUE;
return true;
}
JSBool JSI_GUIMouse::toString(JSContext* cx, uint argc, jsval* vp)
bool JSI_GUIMouse::toString(JSContext* cx, uint argc, jsval* vp)
{
UNUSED2(argc);
JS::CallReceiver rec = JS::CallReceiverFromVp(vp);
@ -285,14 +254,14 @@ JSBool JSI_GUIMouse::toString(JSContext* cx, uint argc, jsval* vp)
char buffer[256];
snprintf(buffer, 256, "%d %d %d", x, y, buttons);
rec.rval().setString(JS_NewStringCopyZ(cx, buffer));
return JS_TRUE;
return true;
}
// Initialise all the types at once:
void JSI_GUITypes::init(ScriptInterface& scriptInterface)
{
scriptInterface.DefineCustomObjectType(&JSI_GUISize::JSI_class, JSI_GUISize::construct, 1, JSI_GUISize::JSI_props, JSI_GUISize::JSI_methods, NULL, NULL);
scriptInterface.DefineCustomObjectType(&JSI_GUIColor::JSI_class, JSI_GUIColor::construct, 1, JSI_GUIColor::JSI_props, JSI_GUIColor::JSI_methods, NULL, NULL);
scriptInterface.DefineCustomObjectType(&JSI_GUIMouse::JSI_class, JSI_GUIMouse::construct, 1, JSI_GUIMouse::JSI_props, JSI_GUIMouse::JSI_methods, NULL, NULL);
scriptInterface.DefineCustomObjectType(&JSI_GUISize::JSI_class, JSI_GUISize::construct, 1, nullptr, JSI_GUISize::JSI_methods, NULL, NULL);
scriptInterface.DefineCustomObjectType(&JSI_GUIColor::JSI_class, JSI_GUIColor::construct, 1, nullptr, JSI_GUIColor::JSI_methods, NULL, NULL);
scriptInterface.DefineCustomObjectType(&JSI_GUIMouse::JSI_class, JSI_GUIMouse::construct, 1, nullptr, JSI_GUIMouse::JSI_methods, NULL, NULL);
}

View File

@ -26,8 +26,8 @@
extern JSClass JSI_class; \
extern JSPropertySpec JSI_props[]; \
extern JSFunctionSpec JSI_methods[]; \
JSBool construct(JSContext* cx, uint argc, jsval* vp); \
JSBool toString(JSContext* cx, uint argc, jsval* vp); \
bool construct(JSContext* cx, uint argc, jsval* vp); \
bool toString(JSContext* cx, uint argc, jsval* vp); \
}
GUISTDTYPE(Size)

View File

@ -36,7 +36,7 @@ JSClass JSI_IGUIObject::JSI_class = {
JSI_IGUIObject::getProperty, JSI_IGUIObject::setProperty,
JS_EnumerateStub, JS_ResolveStub,
JS_ConvertStub, NULL,
NULL, NULL, NULL, JSI_IGUIObject::construct
NULL, NULL, JSI_IGUIObject::construct, NULL
};
JSPropertySpec JSI_IGUIObject::JSI_props[] =
@ -53,22 +53,22 @@ JSFunctionSpec JSI_IGUIObject::JSI_methods[] =
JS_FS_END
};
JSBool JSI_IGUIObject::getProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::MutableHandleValue vp)
bool JSI_IGUIObject::getProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::MutableHandleValue vp)
{
JSAutoRequest rq(cx);
ScriptInterface* pScriptInterface = ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface;
IGUIObject* e = (IGUIObject*)JS_GetInstancePrivate(cx, obj, &JSI_IGUIObject::JSI_class, NULL);
if (!e)
return JS_FALSE;
return false;
JS::RootedValue idval(cx);
if (!JS_IdToValue(cx, id, idval.address()))
return JS_FALSE;
if (!JS_IdToValue(cx, id, &idval))
return false;
std::string propName;
if (!ScriptInterface::FromJSVal(cx, idval, propName))
return JS_FALSE;
return false;
// Skip some things which are known to be functions rather than properties.
// ("constructor" *must* be here, else it'll try to GetSettingType before
@ -83,18 +83,18 @@ JSBool JSI_IGUIObject::getProperty(JSContext* cx, JS::HandleObject obj, JS::Hand
propName == "blur" ||
propName == "getComputedSize"
)
return JS_TRUE;
return true;
// Use onWhatever to access event handlers
if (propName.substr(0, 2) == "on")
{
CStr eventName (CStr(propName.substr(2)).LowerCase());
std::map<CStr, CScriptValRooted>::iterator it = e->m_ScriptHandlers.find(eventName);
auto it = e->m_ScriptHandlers.find(eventName);
if (it == e->m_ScriptHandlers.end())
vp.set(JS::NullValue());
vp.setNull();
else
vp.set((*it).second.get());
return JS_TRUE;
vp.setObject(*it->second.get());
return true;
}
@ -112,13 +112,13 @@ JSBool JSI_IGUIObject::getProperty(JSContext* cx, JS::HandleObject obj, JS::Hand
// Return null if there's no parent
vp.set(JS::NullValue());
}
return JS_TRUE;
return true;
}
// Also handle "name" specially
else if (propName == "name")
{
vp.set(JS::StringValue(JS_NewStringCopyZ(cx, e->GetName().c_str())));
return JS_TRUE;
return true;
}
// Handle all other properties
else
@ -128,7 +128,7 @@ JSBool JSI_IGUIObject::getProperty(JSContext* cx, JS::HandleObject obj, JS::Hand
if (e->GetSettingType(propName, Type) != PSRETURN_OK)
{
JS_ReportError(cx, "Invalid GUIObject property '%s'", propName.c_str());
return JS_FALSE;
return false;
}
// (All the cases are in {...} to avoid scoping problems)
@ -170,7 +170,7 @@ JSBool JSI_IGUIObject::getProperty(JSContext* cx, JS::HandleObject obj, JS::Hand
#define P(x) c = JS::NumberValue(colour.x); \
if (c.isNull()) \
return false; \
JS_SetProperty(cx, obj, #x, c.address())
JS_SetProperty(cx, obj, #x, c)
P(r);
P(g);
P(b);
@ -278,14 +278,14 @@ JSBool JSI_IGUIObject::getProperty(JSContext* cx, JS::HandleObject obj, JS::Hand
CGUIList value;
GUI<CGUIList>::GetSetting(e, propName, value);
JS::RootedObject obj(cx, JS_NewArrayObject(cx, 0, NULL));
JS::RootedObject obj(cx, JS_NewArrayObject(cx, JS::HandleValueArray::empty()));
vp.setObject(*obj);
for (u32 i = 0; i < value.m_Items.size(); ++i)
{
JS::RootedValue val(cx);
ScriptInterface::ToJSVal(cx, &val, value.m_Items[i].GetOriginalString());
JS_SetElement(cx, obj, i, val.address());
JS_SetElement(cx, obj, i, val);
}
break;
@ -294,35 +294,35 @@ JSBool JSI_IGUIObject::getProperty(JSContext* cx, JS::HandleObject obj, JS::Hand
default:
JS_ReportError(cx, "Setting '%s' uses an unimplemented type", propName.c_str());
DEBUG_WARN_ERR(ERR::LOGIC);
return JS_FALSE;
return false;
}
return JS_TRUE;
return true;
}
}
JSBool JSI_IGUIObject::setProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JSBool UNUSED(strict), JS::MutableHandleValue vp)
bool JSI_IGUIObject::setProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool UNUSED(strict), JS::MutableHandleValue vp)
{
IGUIObject* e = (IGUIObject*)JS_GetInstancePrivate(cx, obj, &JSI_IGUIObject::JSI_class, NULL);
if (!e)
return JS_FALSE;
return false;
JSAutoRequest rq(cx);
JS::RootedValue idval(cx);
if (!JS_IdToValue(cx, id, idval.address()))
return JS_FALSE;
if (!JS_IdToValue(cx, id, &idval))
return false;
std::string propName;
if (!ScriptInterface::FromJSVal(cx, idval, propName))
return JS_FALSE;
return false;
if (propName == "name")
{
std::string value;
if (!ScriptInterface::FromJSVal(cx, vp, value))
return JS_FALSE;
return false;
e->SetName(value);
return JS_TRUE;
return true;
}
// Use onWhatever to set event handlers
@ -331,14 +331,14 @@ JSBool JSI_IGUIObject::setProperty(JSContext* cx, JS::HandleObject obj, JS::Hand
if (vp.isPrimitive() || vp.isNull() || !JS_ObjectIsFunction(cx, &vp.toObject()))
{
JS_ReportError(cx, "on- event-handlers must be functions");
return JS_FALSE;
return false;
}
JS::RootedObject vpObj(cx, &vp.toObject());
CStr eventName (CStr(propName.substr(2)).LowerCase());
e->SetScriptHandler(eventName, vpObj);
return JS_TRUE;
return true;
}
// Retrieve the setting's type (and make sure it actually exists)
@ -346,9 +346,13 @@ JSBool JSI_IGUIObject::setProperty(JSContext* cx, JS::HandleObject obj, JS::Hand
if (e->GetSettingType(propName, Type) != PSRETURN_OK)
{
JS_ReportError(cx, "Invalid setting '%s'", propName.c_str());
return JS_TRUE;
return true;
}
JS::RootedObject vpObj(cx);
if (vp.isObject())
vpObj = &vp.toObject();
switch (Type)
{
@ -356,7 +360,7 @@ JSBool JSI_IGUIObject::setProperty(JSContext* cx, JS::HandleObject obj, JS::Hand
{
std::string value;
if (!ScriptInterface::FromJSVal(cx, vp, value))
return JS_FALSE;
return false;
GUI<CStr>::SetSetting(e, propName, value);
break;
@ -386,7 +390,7 @@ JSBool JSI_IGUIObject::setProperty(JSContext* cx, JS::HandleObject obj, JS::Hand
{
std::wstring value;
if (!ScriptInterface::FromJSVal(cx, vp, value))
return JS_FALSE;
return false;
CGUIString str;
str.SetValue(value);
@ -398,7 +402,7 @@ JSBool JSI_IGUIObject::setProperty(JSContext* cx, JS::HandleObject obj, JS::Hand
{
std::string value;
if (!ScriptInterface::FromJSVal(cx, vp, value))
return JS_FALSE;
return false;
EAlign a;
if (value == "left") a = EAlign_Left;
@ -407,7 +411,7 @@ JSBool JSI_IGUIObject::setProperty(JSContext* cx, JS::HandleObject obj, JS::Hand
else
{
JS_ReportError(cx, "Invalid alignment (should be 'left', 'right' or 'center')");
return JS_FALSE;
return false;
}
GUI<EAlign>::SetSetting(e, propName, a);
break;
@ -417,7 +421,7 @@ JSBool JSI_IGUIObject::setProperty(JSContext* cx, JS::HandleObject obj, JS::Hand
{
std::string value;
if (!ScriptInterface::FromJSVal(cx, vp, value))
return JS_FALSE;
return false;
EVAlign a;
if (value == "top") a = EVAlign_Top;
@ -426,7 +430,7 @@ JSBool JSI_IGUIObject::setProperty(JSContext* cx, JS::HandleObject obj, JS::Hand
else
{
JS_ReportError(cx, "Invalid alignment (should be 'top', 'bottom' or 'center')");
return JS_FALSE;
return false;
}
GUI<EVAlign>::SetSetting(e, propName, a);
break;
@ -440,7 +444,7 @@ JSBool JSI_IGUIObject::setProperty(JSContext* cx, JS::HandleObject obj, JS::Hand
else
{
JS_ReportError(cx, "Cannot convert value to int");
return JS_FALSE;
return false;
}
break;
}
@ -448,26 +452,20 @@ JSBool JSI_IGUIObject::setProperty(JSContext* cx, JS::HandleObject obj, JS::Hand
case GUIST_float:
{
double value;
if (JS_ValueToNumber(cx, vp, &value) == true)
if (JS::ToNumber(cx, vp, &value) == true)
GUI<float>::SetSetting(e, propName, (float)value);
else
{
JS_ReportError(cx, "Cannot convert value to float");
return JS_FALSE;
return false;
}
break;
}
case GUIST_bool:
{
JSBool value;
if (JS_ValueToBoolean(cx, vp, &value))
GUI<bool>::SetSetting(e, propName, value);
else
{
JS_ReportError(cx, "Cannot convert value to bool");
return JS_FALSE;
}
bool value = JS::ToBoolean(vp);
GUI<bool>::SetSetting(e, propName, value);
break;
}
@ -477,15 +475,15 @@ JSBool JSI_IGUIObject::setProperty(JSContext* cx, JS::HandleObject obj, JS::Hand
{
std::wstring value;
if (!ScriptInterface::FromJSVal(cx, vp, value))
return JS_FALSE;
return false;
if (e->SetSetting(propName, value) != PSRETURN_OK)
{
JS_ReportError(cx, "Invalid value for setting '%s'", propName.c_str());
return JS_FALSE;
return false;
}
}
else if (vp.isObject() && JS_InstanceOf(cx, &vp.toObject(), &JSI_GUISize::JSI_class, NULL))
else if (vp.isObject() && JS_InstanceOf(cx, vpObj, &JSI_GUISize::JSI_class, NULL))
{
CClientArea area;
GUI<CClientArea>::GetSetting(e, propName, area);
@ -507,7 +505,7 @@ JSBool JSI_IGUIObject::setProperty(JSContext* cx, JS::HandleObject obj, JS::Hand
else
{
JS_ReportError(cx, "Size only accepts strings or GUISize objects");
return JS_FALSE;
return false;
}
break;
}
@ -518,21 +516,21 @@ JSBool JSI_IGUIObject::setProperty(JSContext* cx, JS::HandleObject obj, JS::Hand
{
std::wstring value;
if (!ScriptInterface::FromJSVal(cx, vp, value))
return JS_FALSE;
return false;
if (e->SetSetting(propName, value) != PSRETURN_OK)
{
JS_ReportError(cx, "Invalid value for setting '%s'", propName.c_str());
return JS_FALSE;
return false;
}
}
else if (vp.isObject() && JS_InstanceOf(cx, &vp.toObject(), &JSI_GUIColor::JSI_class, NULL))
else if (vp.isObject() && JS_InstanceOf(cx, vpObj, &JSI_GUIColor::JSI_class, NULL))
{
CColor colour;
JS::RootedObject (cx, &vp.toObject());
JS::RootedValue t(cx);
double s;
#define PROP(x) JS_GetProperty(cx, obj, #x, t.address()); \
#define PROP(x) JS_GetProperty(cx, obj, #x, &t); \
s = t.toDouble(); \
colour.x = (float)s
PROP(r); PROP(g); PROP(b); PROP(a);
@ -543,7 +541,7 @@ JSBool JSI_IGUIObject::setProperty(JSContext* cx, JS::HandleObject obj, JS::Hand
else
{
JS_ReportError(cx, "Color only accepts strings or GUIColor objects");
return JS_FALSE;
return false;
}
break;
}
@ -551,37 +549,35 @@ JSBool JSI_IGUIObject::setProperty(JSContext* cx, JS::HandleObject obj, JS::Hand
case GUIST_CGUIList:
{
u32 length;
if (vp.isObject() && JS_GetArrayLength(cx, &vp.toObject(), &length) == true)
{
CGUIList list;
JS::RootedObject obj(cx, &vp.toObject());
for (u32 i=0; i<length; ++i)
{
JS::RootedValue element(cx);
if (! JS_GetElement(cx, obj, i, element.address()))
{
JS_ReportError(cx, "Failed to get list element");
return JS_FALSE;
}
std::wstring value;
if (!ScriptInterface::FromJSVal(cx, element, value))
return JS_FALSE;
CGUIString str;
str.SetValue(value);
list.m_Items.push_back(str);
}
GUI<CGUIList>::SetSetting(e, propName, list);
}
else
if (!vp.isObject() || !JS_GetArrayLength(cx, vpObj, &length))
{
JS_ReportError(cx, "List only accepts a GUIList object");
return JS_FALSE;
return false;
}
CGUIList list;
JS::RootedObject obj(cx, &vp.toObject());
for (u32 i=0; i<length; ++i)
{
JS::RootedValue element(cx);
if (! JS_GetElement(cx, obj, i, &element))
{
JS_ReportError(cx, "Failed to get list element");
return false;
}
std::wstring value;
if (!ScriptInterface::FromJSVal(cx, element, value))
return false;
CGUIString str;
str.SetValue(value);
list.m_Items.push_back(str);
}
GUI<CGUIList>::SetSetting(e, propName, list);
break;
}
@ -596,7 +592,7 @@ JSBool JSI_IGUIObject::setProperty(JSContext* cx, JS::HandleObject obj, JS::Hand
}
JSBool JSI_IGUIObject::construct(JSContext* cx, uint argc, jsval* vp)
bool JSI_IGUIObject::construct(JSContext* cx, uint argc, jsval* vp)
{
JSAutoRequest rq(cx);
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
@ -605,7 +601,7 @@ JSBool JSI_IGUIObject::construct(JSContext* cx, uint argc, jsval* vp)
if (args.length() == 0)
{
JS_ReportError(cx, "GUIObject has no default constructor");
return JS_FALSE;
return false;
}
JS::RootedObject obj(cx, pScriptInterface->CreateCustomObject("GUIObject"));
@ -615,7 +611,7 @@ JSBool JSI_IGUIObject::construct(JSContext* cx, uint argc, jsval* vp)
JS_SetPrivate(obj, guiObject);
args.rval().setObject(*obj);
return JS_TRUE;
return true;
}
void JSI_IGUIObject::init(ScriptInterface& scriptInterface)
@ -623,7 +619,7 @@ void JSI_IGUIObject::init(ScriptInterface& scriptInterface)
scriptInterface.DefineCustomObjectType(&JSI_class, construct, 1, JSI_props, JSI_methods, NULL, NULL);
}
JSBool JSI_IGUIObject::toString(JSContext* cx, uint argc, jsval* vp)
bool JSI_IGUIObject::toString(JSContext* cx, uint argc, jsval* vp)
{
UNUSED2(argc);
JSAutoRequest rq(cx);
@ -633,16 +629,16 @@ JSBool JSI_IGUIObject::toString(JSContext* cx, uint argc, jsval* vp)
IGUIObject* e = (IGUIObject*)JS_GetInstancePrivate(cx, thisObj, &JSI_IGUIObject::JSI_class, NULL);
if (!e)
return JS_FALSE;
return false;
char buffer[256];
snprintf(buffer, 256, "[GUIObject: %s]", e->GetName().c_str());
buffer[255] = 0;
rec.rval().setString(JS_NewStringCopyZ(cx, buffer));
return JS_TRUE;
return true;
}
JSBool JSI_IGUIObject::focus(JSContext* cx, uint argc, jsval* vp)
bool JSI_IGUIObject::focus(JSContext* cx, uint argc, jsval* vp)
{
UNUSED2(argc);
JSAutoRequest rq(cx);
@ -652,15 +648,15 @@ JSBool JSI_IGUIObject::focus(JSContext* cx, uint argc, jsval* vp)
IGUIObject* e = (IGUIObject*)JS_GetInstancePrivate(cx, thisObj, &JSI_IGUIObject::JSI_class, NULL);
if (!e)
return JS_FALSE;
return false;
e->GetGUI()->SetFocusedObject(e);
rec.rval().setUndefined();
return JS_TRUE;
return true;
}
JSBool JSI_IGUIObject::blur(JSContext* cx, uint argc, jsval* vp)
bool JSI_IGUIObject::blur(JSContext* cx, uint argc, jsval* vp)
{
UNUSED2(argc);
JSAutoRequest rq(cx);
@ -670,15 +666,15 @@ JSBool JSI_IGUIObject::blur(JSContext* cx, uint argc, jsval* vp)
IGUIObject* e = (IGUIObject*)JS_GetInstancePrivate(cx, thisObj, &JSI_IGUIObject::JSI_class, NULL);
if (!e)
return JS_FALSE;
return false;
e->GetGUI()->SetFocusedObject(NULL);
rec.rval().setUndefined();
return JS_TRUE;
return true;
}
JSBool JSI_IGUIObject::getComputedSize(JSContext* cx, uint argc, jsval* vp)
bool JSI_IGUIObject::getComputedSize(JSContext* cx, uint argc, jsval* vp)
{
UNUSED2(argc);
JSAutoRequest rq(cx);
@ -688,12 +684,12 @@ JSBool JSI_IGUIObject::getComputedSize(JSContext* cx, uint argc, jsval* vp)
IGUIObject* e = (IGUIObject*)JS_GetInstancePrivate(cx, thisObj, &JSI_IGUIObject::JSI_class, NULL);
if (!e)
return JS_FALSE;
return false;
e->UpdateCachedSize();
CRect size = e->m_CachedActualSize;
JS::RootedValue objVal(cx, JS::ObjectValue(*JS_NewObject(cx, NULL, NULL, NULL)));
JS::RootedValue objVal(cx, JS::ObjectValue(*JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr())));
try
{
ScriptInterface* pScriptInterface = ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface;
@ -705,9 +701,9 @@ JSBool JSI_IGUIObject::getComputedSize(JSContext* cx, uint argc, jsval* vp)
catch (PSERROR_Scripting_ConversionFailed&)
{
debug_warn(L"Error creating size object!");
return JS_FALSE;
return false;
}
rec.rval().set(objVal);
return JS_TRUE;
return true;
}

View File

@ -25,13 +25,13 @@ namespace JSI_IGUIObject
extern JSClass JSI_class;
extern JSPropertySpec JSI_props[];
extern JSFunctionSpec JSI_methods[];
JSBool getProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::MutableHandleValue vp);
JSBool setProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JSBool UNUSED(strict), JS::MutableHandleValue vp);
JSBool construct(JSContext* cx, uint argc, jsval* vp);
JSBool toString(JSContext* cx, uint argc, jsval* vp);
JSBool focus(JSContext* cx, uint argc, jsval* vp);
JSBool blur(JSContext* cx, uint argc, jsval* vp);
JSBool getComputedSize(JSContext* cx, uint argc, jsval* vp);
bool getProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::MutableHandleValue vp);
bool setProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool UNUSED(strict), JS::MutableHandleValue vp);
bool construct(JSContext* cx, uint argc, jsval* vp);
bool toString(JSContext* cx, uint argc, jsval* vp);
bool focus(JSContext* cx, uint argc, jsval* vp);
bool blur(JSContext* cx, uint argc, jsval* vp);
bool getComputedSize(JSContext* cx, uint argc, jsval* vp);
void init(ScriptInterface& scriptInterface);
}

View File

@ -86,23 +86,13 @@ namespace {
// Note that the initData argument may only contain clonable data.
// Functions aren't supported for example!
// TODO: Use LOGERROR to print a friendly error message when the requirements aren't met instead of failing with debug_warn when cloning.
void PushGuiPage(ScriptInterface::CxPrivate* pCxPrivate, std::wstring name, CScriptVal initData1)
void PushGuiPage(ScriptInterface::CxPrivate* pCxPrivate, std::wstring name, JS::HandleValue initData)
{
JSContext* cx = pCxPrivate->pScriptInterface->GetContext();
JSAutoRequest rq(cx);
// TODO: Get Handle parameter directly with SpiderMonkey 31
JS::RootedValue initData(cx, initData1.get());
g_GUI->PushPage(name, pCxPrivate->pScriptInterface->WriteStructuredClone(initData));
}
void SwitchGuiPage(ScriptInterface::CxPrivate* pCxPrivate, std::wstring name, CScriptVal initData1)
void SwitchGuiPage(ScriptInterface::CxPrivate* pCxPrivate, std::wstring name, JS::HandleValue initData)
{
JSContext* cx = pCxPrivate->pScriptInterface->GetContext();
JSAutoRequest rq(cx);
// TODO: Get Handle parameter directly with SpiderMonkey 31
JS::RootedValue initData(cx, initData1.get());
g_GUI->SwitchPage(name, pCxPrivate->pScriptInterface, initData);
}
@ -114,23 +104,13 @@ void PopGuiPage(ScriptInterface::CxPrivate* UNUSED(pCxPrivate))
// Note that the args argument may only contain clonable data.
// Functions aren't supported for example!
// TODO: Use LOGERROR to print a friendly error message when the requirements aren't met instead of failing with debug_warn when cloning.
void PopGuiPageCB(ScriptInterface::CxPrivate* pCxPrivate, CScriptVal args1)
void PopGuiPageCB(ScriptInterface::CxPrivate* pCxPrivate, JS::HandleValue args)
{
JSContext* cx = pCxPrivate->pScriptInterface->GetContext();
JSAutoRequest rq(cx);
// TODO: Get Handle parameter directly with SpiderMonkey 31
JS::RootedValue args(cx, args1.get());
g_GUI->PopPageCB(pCxPrivate->pScriptInterface->WriteStructuredClone(args));
}
CScriptVal GuiInterfaceCall(ScriptInterface::CxPrivate* pCxPrivate, std::wstring name, CScriptVal data1)
JS::Value GuiInterfaceCall(ScriptInterface::CxPrivate* pCxPrivate, std::wstring name, JS::HandleValue data)
{
// TODO: With ESR31 we should be able to take JS::HandleValue directly
JSContext* cx = pCxPrivate->pScriptInterface->GetContext();
JSAutoRequest rq(cx);
JS::RootedValue data(cx, data1.get());
if (!g_Game)
return JS::UndefinedValue();
CSimulation2* sim = g_Game->GetSimulation2();
@ -145,12 +125,13 @@ CScriptVal GuiInterfaceCall(ScriptInterface::CxPrivate* pCxPrivate, std::wstring
JSContext* cxSim = sim->GetScriptInterface().GetContext();
JSAutoRequest rqSim(cxSim);
JS::RootedValue arg(cxSim, sim->GetScriptInterface().CloneValueFromOtherContext(*(pCxPrivate->pScriptInterface), data));
JS::RootedValue ret(cxSim, cmpGuiInterface->ScriptCall(player, name, CScriptVal(arg)).get());
JS::RootedValue ret(cxSim);
cmpGuiInterface->ScriptCall(player, name, arg, &ret);
return pCxPrivate->pScriptInterface->CloneValueFromOtherContext(sim->GetScriptInterface(), ret);
}
void PostNetworkCommand(ScriptInterface::CxPrivate* pCxPrivate, CScriptVal cmd1)
void PostNetworkCommand(ScriptInterface::CxPrivate* pCxPrivate, JS::HandleValue cmd)
{
if (!g_Game)
return;
@ -160,17 +141,12 @@ void PostNetworkCommand(ScriptInterface::CxPrivate* pCxPrivate, CScriptVal cmd1)
CmpPtr<ICmpCommandQueue> cmpCommandQueue(*sim, SYSTEM_ENTITY);
if (!cmpCommandQueue)
return;
// TODO: With ESR31 we should be able to take JS::HandleValue directly
JSContext* cx = pCxPrivate->pScriptInterface->GetContext();
JSAutoRequest rq(cx);
JS::RootedValue cmd(cx, cmd1.get());
JSContext* cxSim = sim->GetScriptInterface().GetContext();
JSAutoRequest rqSim(cxSim);
JS::RootedValue cmd2(cxSim, sim->GetScriptInterface().CloneValueFromOtherContext(*(pCxPrivate->pScriptInterface), cmd));
cmpCommandQueue->PostNetworkCommand(CScriptVal(cmd2));
cmpCommandQueue->PostNetworkCommand(cmd2);
}
entity_id_t PickEntityAtPoint(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), int x, int y)
@ -219,7 +195,7 @@ void SetPlayerID(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), int id)
g_Game->SetPlayerID(id);
}
CScriptValRooted GetEngineInfo(ScriptInterface::CxPrivate* pCxPrivate)
JS::Value GetEngineInfo(ScriptInterface::CxPrivate* pCxPrivate)
{
return SavedGames::GetEngineInfo(*(pCxPrivate->pScriptInterface));
}
@ -230,18 +206,13 @@ void StartNetworkGame(ScriptInterface::CxPrivate* UNUSED(pCxPrivate))
g_NetServer->StartGame();
}
void StartGame(ScriptInterface::CxPrivate* pCxPrivate, CScriptVal attribs1, int playerID)
void StartGame(ScriptInterface::CxPrivate* pCxPrivate, JS::HandleValue attribs, int playerID)
{
ENSURE(!g_NetServer);
ENSURE(!g_NetClient);
ENSURE(!g_Game);
g_Game = new CGame();
// TODO: With ESR31 we should be able to take JS::HandleValue directly
JSContext* cx = pCxPrivate->pScriptInterface->GetContext();
JSAutoRequest rq(cx);
JS::RootedValue attribs(cx, attribs1.get());
// Convert from GUI script context to sim script context
CSimulation2* sim = g_Game->GetSimulation2();
@ -252,10 +223,10 @@ void StartGame(ScriptInterface::CxPrivate* pCxPrivate, CScriptVal attribs1, int
sim->GetScriptInterface().CloneValueFromOtherContext(*(pCxPrivate->pScriptInterface), attribs));
g_Game->SetPlayerID(playerID);
g_Game->StartGame(CScriptValRooted(cxSim, gameAttribs), "");
g_Game->StartGame(&gameAttribs, "");
}
CScriptVal StartSavedGame(ScriptInterface::CxPrivate* pCxPrivate, std::wstring name)
JS::Value StartSavedGame(ScriptInterface::CxPrivate* pCxPrivate, std::wstring name)
{
// We need to be careful with different compartments and contexts.
// The GUI calls this function from the GUI context and expects the return value in the same context.
@ -293,43 +264,34 @@ CScriptVal StartSavedGame(ScriptInterface::CxPrivate* pCxPrivate, std::wstring n
// Start the game
g_Game->SetPlayerID(playerID);
g_Game->StartGame(CScriptValRooted(cxGame, gameInitAttributes), savedState);
g_Game->StartGame(&gameInitAttributes, savedState);
}
return guiContextMetadata.get();
return guiContextMetadata;
}
void SaveGame(ScriptInterface::CxPrivate* pCxPrivate, std::wstring filename, std::wstring description, CScriptVal GUIMetadata1)
void SaveGame(ScriptInterface::CxPrivate* pCxPrivate, std::wstring filename, std::wstring description, JS::HandleValue GUIMetadata)
{
JSContext* cx = pCxPrivate->pScriptInterface->GetContext();
JSAutoRequest rq(cx);
// TODO: Get Handle parameter directly with SpiderMonkey 31
JS::RootedValue GUIMetadata(cx, GUIMetadata1.get());
shared_ptr<ScriptInterface::StructuredClone> GUIMetadataClone = pCxPrivate->pScriptInterface->WriteStructuredClone(GUIMetadata);
if (SavedGames::Save(filename, description, *g_Game->GetSimulation2(), GUIMetadataClone, g_Game->GetPlayerID()) < 0)
LOGERROR("Failed to save game");
}
void SaveGamePrefix(ScriptInterface::CxPrivate* pCxPrivate, std::wstring prefix, std::wstring description, CScriptVal GUIMetadata1)
void SaveGamePrefix(ScriptInterface::CxPrivate* pCxPrivate, std::wstring prefix, std::wstring description, JS::HandleValue GUIMetadata)
{
JSContext* cx = pCxPrivate->pScriptInterface->GetContext();
JSAutoRequest rq(cx);
// TODO: Get Handle parameter directly with SpiderMonkey 31
JS::RootedValue GUIMetadata(cx, GUIMetadata1.get());
shared_ptr<ScriptInterface::StructuredClone> GUIMetadataClone = pCxPrivate->pScriptInterface->WriteStructuredClone(GUIMetadata);
if (SavedGames::SavePrefix(prefix, description, *g_Game->GetSimulation2(), GUIMetadataClone, g_Game->GetPlayerID()) < 0)
LOGERROR("Failed to save game");
}
void SetNetworkGameAttributes(ScriptInterface::CxPrivate* pCxPrivate, CScriptVal attribs1)
void SetNetworkGameAttributes(ScriptInterface::CxPrivate* pCxPrivate, JS::HandleValue attribs1)
{
ENSURE(g_NetServer);
// TODO: Get Handle parameter directly with SpiderMonkey 31
//TODO: This is a workaround because we need to pass a MutableHandle to a JSAPI functions somewhere
// (with no obvious reason).
JSContext* cx = pCxPrivate->pScriptInterface->GetContext();
JSAutoRequest rq(cx);
JS::RootedValue attribs(cx, attribs1.get());
JS::RootedValue attribs(cx, attribs1);
g_NetServer->UpdateGameAttributes(&attribs, *(pCxPrivate->pScriptInterface));
}
@ -386,7 +348,7 @@ void DisconnectNetworkGame(ScriptInterface::CxPrivate* UNUSED(pCxPrivate))
SAFE_DELETE(g_Game);
}
CScriptVal PollNetworkClient(ScriptInterface::CxPrivate* pCxPrivate)
JS::Value PollNetworkClient(ScriptInterface::CxPrivate* pCxPrivate)
{
if (!g_NetClient)
return JS::UndefinedValue();
@ -434,12 +396,12 @@ void SendNetworkReady(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), int messag
g_NetClient->SendReadyMessage(message);
}
std::vector<CScriptValRooted> GetAIs(ScriptInterface::CxPrivate* pCxPrivate)
JS::Value GetAIs(ScriptInterface::CxPrivate* pCxPrivate)
{
return ICmpAIManager::GetAIs(*(pCxPrivate->pScriptInterface));
}
std::vector<CScriptValRooted> GetSavedGames(ScriptInterface::CxPrivate* pCxPrivate)
JS::Value GetSavedGames(ScriptInterface::CxPrivate* pCxPrivate)
{
return SavedGames::GetSavedGames(*(pCxPrivate->pScriptInterface));
}
@ -474,17 +436,22 @@ bool IsAtlasRunning(ScriptInterface::CxPrivate* UNUSED(pCxPrivate))
return (g_AtlasGameLoop && g_AtlasGameLoop->running);
}
CScriptVal LoadMapSettings(ScriptInterface::CxPrivate* pCxPrivate, VfsPath pathname)
JS::Value LoadMapSettings(ScriptInterface::CxPrivate* pCxPrivate, VfsPath pathname)
{
JSContext* cx = pCxPrivate->pScriptInterface->GetContext();
JSAutoRequest rq(cx);
CMapSummaryReader reader;
if (reader.LoadMap(pathname) != PSRETURN_OK)
return CScriptVal();
return JS::UndefinedValue();
return reader.GetMapSettings(*(pCxPrivate->pScriptInterface)).get();
JS::RootedValue settings(cx);
reader.GetMapSettings(*(pCxPrivate->pScriptInterface), &settings);
return settings;
}
CScriptVal GetMapSettings(ScriptInterface::CxPrivate* pCxPrivate)
JS::Value GetMapSettings(ScriptInterface::CxPrivate* pCxPrivate)
{
if (!g_Game)
return JS::UndefinedValue();
@ -492,7 +459,8 @@ CScriptVal GetMapSettings(ScriptInterface::CxPrivate* pCxPrivate)
JSContext* cx = g_Game->GetSimulation2()->GetScriptInterface().GetContext();
JSAutoRequest rq(cx);
JS::RootedValue mapSettings(cx, g_Game->GetSimulation2()->GetMapSettings().get());
JS::RootedValue mapSettings(cx);
g_Game->GetSimulation2()->GetMapSettings(&mapSettings);
return pCxPrivate->pScriptInterface->CloneValueFromOtherContext(
g_Game->GetSimulation2()->GetScriptInterface(),
mapSettings);
@ -590,7 +558,7 @@ void DisplayErrorDialog(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), std::wst
debug_DisplayError(msg.c_str(), DE_NO_DEBUG_INFO, NULL, NULL, NULL, 0, NULL, NULL);
}
CScriptVal GetProfilerState(ScriptInterface::CxPrivate* pCxPrivate)
JS::Value GetProfilerState(ScriptInterface::CxPrivate* pCxPrivate)
{
return g_ProfileViewer.SaveToJS(*(pCxPrivate->pScriptInterface));
}
@ -761,13 +729,13 @@ int GetFps(ScriptInterface::CxPrivate* UNUSED(pCxPrivate))
return freq;
}
CScriptVal GetGUIObjectByName(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), CStr name)
JS::Value GetGUIObjectByName(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), CStr name)
{
IGUIObject* guiObj = g_GUI->FindObjectByName(name);
if (guiObj)
return OBJECT_TO_JSVAL(guiObj->GetJSObject());
return JS::ObjectValue(*guiObj->GetJSObject());
else
return JSVAL_VOID;
return JS::UndefinedValue();
}
// Return the date/time at which the current executable was compiled.
@ -833,18 +801,23 @@ std::wstring GetBuildTimestamp(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), i
return wstring_from_utf8(buf);
}
CScriptVal ReadJSONFile(ScriptInterface::CxPrivate* pCxPrivate, std::wstring filePath)
JS::Value ReadJSONFile(ScriptInterface::CxPrivate* pCxPrivate, std::wstring filePath)
{
JSContext* cx = pCxPrivate->pScriptInterface->GetContext();
JSAutoRequest rq(cx);
JS::RootedValue out(cx);
pCxPrivate->pScriptInterface->ReadJSONFile(filePath, &out);
return out.get();
return out;
}
void WriteJSONFile(ScriptInterface::CxPrivate* pCxPrivate, std::wstring filePath, CScriptVal scriptVal)
void WriteJSONFile(ScriptInterface::CxPrivate* pCxPrivate, std::wstring filePath, JS::HandleValue val1)
{
JS::RootedValue val(pCxPrivate->pScriptInterface->GetContext(), scriptVal.get());
JSContext* cx = pCxPrivate->pScriptInterface->GetContext();
JSAutoRequest rq(cx);
// TODO: This is a workaround because we need to pass a MutableHandle to StringifyJSON.
JS::RootedValue val(cx, val1);
std::string str(pCxPrivate->pScriptInterface->StringifyJSON(&val, false));
VfsPath path(filePath);
@ -941,22 +914,22 @@ void GuiScriptingInit(ScriptInterface& scriptInterface)
JSI_Lobby::RegisterScriptFunctions(scriptInterface);
// VFS (external)
scriptInterface.RegisterFunction<CScriptVal, std::wstring, std::wstring, bool, &JSI_VFS::BuildDirEntList>("BuildDirEntList");
scriptInterface.RegisterFunction<JS::Value, std::wstring, std::wstring, bool, &JSI_VFS::BuildDirEntList>("BuildDirEntList");
scriptInterface.RegisterFunction<bool, CStrW, JSI_VFS::FileExists>("FileExists");
scriptInterface.RegisterFunction<double, std::wstring, &JSI_VFS::GetFileMTime>("GetFileMTime");
scriptInterface.RegisterFunction<unsigned int, std::wstring, &JSI_VFS::GetFileSize>("GetFileSize");
scriptInterface.RegisterFunction<CScriptVal, std::wstring, &JSI_VFS::ReadFile>("ReadFile");
scriptInterface.RegisterFunction<CScriptVal, std::wstring, &JSI_VFS::ReadFileLines>("ReadFileLines");
scriptInterface.RegisterFunction<JS::Value, std::wstring, &JSI_VFS::ReadFile>("ReadFile");
scriptInterface.RegisterFunction<JS::Value, std::wstring, &JSI_VFS::ReadFileLines>("ReadFileLines");
// GUI manager functions:
scriptInterface.RegisterFunction<void, std::wstring, CScriptVal, &PushGuiPage>("PushGuiPage");
scriptInterface.RegisterFunction<void, std::wstring, CScriptVal, &SwitchGuiPage>("SwitchGuiPage");
scriptInterface.RegisterFunction<void, std::wstring, JS::HandleValue, &PushGuiPage>("PushGuiPage");
scriptInterface.RegisterFunction<void, std::wstring, JS::HandleValue, &SwitchGuiPage>("SwitchGuiPage");
scriptInterface.RegisterFunction<void, &PopGuiPage>("PopGuiPage");
scriptInterface.RegisterFunction<void, CScriptVal, &PopGuiPageCB>("PopGuiPageCB");
scriptInterface.RegisterFunction<CScriptVal, CStr, &GetGUIObjectByName>("GetGUIObjectByName");
scriptInterface.RegisterFunction<void, JS::HandleValue, &PopGuiPageCB>("PopGuiPageCB");
scriptInterface.RegisterFunction<JS::Value, CStr, &GetGUIObjectByName>("GetGUIObjectByName");
// Simulation<->GUI interface functions:
scriptInterface.RegisterFunction<CScriptVal, std::wstring, CScriptVal, &GuiInterfaceCall>("GuiInterfaceCall");
scriptInterface.RegisterFunction<void, CScriptVal, &PostNetworkCommand>("PostNetworkCommand");
scriptInterface.RegisterFunction<JS::Value, std::wstring, JS::HandleValue, &GuiInterfaceCall>("GuiInterfaceCall");
scriptInterface.RegisterFunction<void, JS::HandleValue, &PostNetworkCommand>("PostNetworkCommand");
// Entity picking
scriptInterface.RegisterFunction<entity_id_t, int, int, &PickEntityAtPoint>("PickEntityAtPoint");
@ -967,27 +940,27 @@ void GuiScriptingInit(ScriptInterface& scriptInterface)
// Network / game setup functions
scriptInterface.RegisterFunction<void, &StartNetworkGame>("StartNetworkGame");
scriptInterface.RegisterFunction<void, CScriptVal, int, &StartGame>("StartGame");
scriptInterface.RegisterFunction<void, JS::HandleValue, int, &StartGame>("StartGame");
scriptInterface.RegisterFunction<void, &Script_EndGame>("EndGame");
scriptInterface.RegisterFunction<void, std::wstring, &StartNetworkHost>("StartNetworkHost");
scriptInterface.RegisterFunction<void, std::wstring, std::string, &StartNetworkJoin>("StartNetworkJoin");
scriptInterface.RegisterFunction<void, &DisconnectNetworkGame>("DisconnectNetworkGame");
scriptInterface.RegisterFunction<CScriptVal, &PollNetworkClient>("PollNetworkClient");
scriptInterface.RegisterFunction<void, CScriptVal, &SetNetworkGameAttributes>("SetNetworkGameAttributes");
scriptInterface.RegisterFunction<JS::Value, &PollNetworkClient>("PollNetworkClient");
scriptInterface.RegisterFunction<void, JS::HandleValue, &SetNetworkGameAttributes>("SetNetworkGameAttributes");
scriptInterface.RegisterFunction<void, int, std::string, &AssignNetworkPlayer>("AssignNetworkPlayer");
scriptInterface.RegisterFunction<void, std::string, int, &SetNetworkPlayerStatus>("SetNetworkPlayerStatus");
scriptInterface.RegisterFunction<void, &ClearAllPlayerReady>("ClearAllPlayerReady");
scriptInterface.RegisterFunction<void, std::wstring, &SendNetworkChat>("SendNetworkChat");
scriptInterface.RegisterFunction<void, int, &SendNetworkReady>("SendNetworkReady");
scriptInterface.RegisterFunction<std::vector<CScriptValRooted>, &GetAIs>("GetAIs");
scriptInterface.RegisterFunction<CScriptValRooted, &GetEngineInfo>("GetEngineInfo");
scriptInterface.RegisterFunction<JS::Value, &GetAIs>("GetAIs");
scriptInterface.RegisterFunction<JS::Value, &GetEngineInfo>("GetEngineInfo");
// Saved games
scriptInterface.RegisterFunction<CScriptVal, std::wstring, &StartSavedGame>("StartSavedGame");
scriptInterface.RegisterFunction<std::vector<CScriptValRooted>, &GetSavedGames>("GetSavedGames");
scriptInterface.RegisterFunction<JS::Value, std::wstring, &StartSavedGame>("StartSavedGame");
scriptInterface.RegisterFunction<JS::Value, &GetSavedGames>("GetSavedGames");
scriptInterface.RegisterFunction<bool, std::wstring, &DeleteSavedGame>("DeleteSavedGame");
scriptInterface.RegisterFunction<void, std::wstring, std::wstring, CScriptVal, &SaveGame>("SaveGame");
scriptInterface.RegisterFunction<void, std::wstring, std::wstring, CScriptVal, &SaveGamePrefix>("SaveGamePrefix");
scriptInterface.RegisterFunction<void, std::wstring, std::wstring, JS::HandleValue, &SaveGame>("SaveGame");
scriptInterface.RegisterFunction<void, std::wstring, std::wstring, JS::HandleValue, &SaveGamePrefix>("SaveGamePrefix");
scriptInterface.RegisterFunction<void, &QuickSave>("QuickSave");
scriptInterface.RegisterFunction<void, &QuickLoad>("QuickLoad");
@ -1000,8 +973,8 @@ void GuiScriptingInit(ScriptInterface& scriptInterface)
scriptInterface.RegisterFunction<void, &RestartInAtlas>("RestartInAtlas");
scriptInterface.RegisterFunction<bool, &AtlasIsAvailable>("AtlasIsAvailable");
scriptInterface.RegisterFunction<bool, &IsAtlasRunning>("IsAtlasRunning");
scriptInterface.RegisterFunction<CScriptVal, VfsPath, &LoadMapSettings>("LoadMapSettings");
scriptInterface.RegisterFunction<CScriptVal, &GetMapSettings>("GetMapSettings");
scriptInterface.RegisterFunction<JS::Value, VfsPath, &LoadMapSettings>("LoadMapSettings");
scriptInterface.RegisterFunction<JS::Value, &GetMapSettings>("GetMapSettings");
scriptInterface.RegisterFunction<float, &CameraGetX>("CameraGetX");
scriptInterface.RegisterFunction<float, &CameraGetZ>("CameraGetZ");
scriptInterface.RegisterFunction<void, entity_id_t, &CameraFollow>("CameraFollow");
@ -1011,14 +984,14 @@ void GuiScriptingInit(ScriptInterface& scriptInterface)
scriptInterface.RegisterFunction<entity_id_t, &GetFollowedEntity>("GetFollowedEntity");
scriptInterface.RegisterFunction<bool, std::string, &HotkeyIsPressed_>("HotkeyIsPressed");
scriptInterface.RegisterFunction<void, std::wstring, &DisplayErrorDialog>("DisplayErrorDialog");
scriptInterface.RegisterFunction<CScriptVal, &GetProfilerState>("GetProfilerState");
scriptInterface.RegisterFunction<JS::Value, &GetProfilerState>("GetProfilerState");
scriptInterface.RegisterFunction<void, &ExitProgram>("Exit");
scriptInterface.RegisterFunction<bool, &IsPaused>("IsPaused");
scriptInterface.RegisterFunction<void, bool, &SetPaused>("SetPaused");
scriptInterface.RegisterFunction<int, &GetFps>("GetFPS");
scriptInterface.RegisterFunction<std::wstring, int, &GetBuildTimestamp>("GetBuildTimestamp");
scriptInterface.RegisterFunction<CScriptVal, std::wstring, &ReadJSONFile>("ReadJSONFile");
scriptInterface.RegisterFunction<void, std::wstring, CScriptVal, &WriteJSONFile>("WriteJSONFile");
scriptInterface.RegisterFunction<JS::Value, std::wstring, &ReadJSONFile>("ReadJSONFile");
scriptInterface.RegisterFunction<void, std::wstring, JS::HandleValue, &WriteJSONFile>("WriteJSONFile");
scriptInterface.RegisterFunction<CParamNode, std::string, &GetTemplate>("GetTemplate");
// User report functions

View File

@ -18,9 +18,9 @@
#ifndef IXMPPCLIENT_H
#define IXMPPCLIENT_H
#include "scriptinterface/ScriptTypes.h"
class ScriptInterface;
class CScriptVal;
class CScriptValRooted;
class IXmppClient
{
@ -35,8 +35,8 @@ public:
virtual void SendIqGetBoardList() = 0;
virtual void SendIqGetRatingList() = 0;
virtual void SendIqGetProfile(const std::string& player) = 0;
virtual void SendIqGameReport(ScriptInterface& scriptInterface, CScriptVal data) = 0;
virtual void SendIqRegisterGame(ScriptInterface& scriptInterface, CScriptVal data) = 0;
virtual void SendIqGameReport(ScriptInterface& scriptInterface, JS::HandleValue data) = 0;
virtual void SendIqRegisterGame(ScriptInterface& scriptInterface, JS::HandleValue data) = 0;
virtual void SendIqUnregisterGame() = 0;
virtual void SendIqChangeStateGame(const std::string& nbp, const std::string& players) = 0;
virtual void SetNick(const std::string& nick) = 0;
@ -48,12 +48,12 @@ public:
virtual void GetRole(const std::string& nickname, std::string& role) = 0;
virtual void GetSubject(std::string& subject) = 0;
virtual CScriptValRooted GUIGetPlayerList(ScriptInterface& scriptInterface) = 0;
virtual CScriptValRooted GUIGetGameList(ScriptInterface& scriptInterface) = 0;
virtual CScriptValRooted GUIGetBoardList(ScriptInterface& scriptInterface) = 0;
virtual CScriptValRooted GUIGetProfile(ScriptInterface& scriptInterface) = 0;
virtual void GUIGetPlayerList(ScriptInterface& scriptInterface, JS::MutableHandleValue ret) = 0;
virtual void GUIGetGameList(ScriptInterface& scriptInterface, JS::MutableHandleValue ret) = 0;
virtual void GUIGetBoardList(ScriptInterface& scriptInterface, JS::MutableHandleValue ret) = 0;
virtual void GUIGetProfile(ScriptInterface& scriptInterface, JS::MutableHandleValue ret) = 0;
virtual CScriptValRooted GuiPollMessage(ScriptInterface& scriptInterface) = 0;
virtual void GuiPollMessage(ScriptInterface& scriptInterface, JS::MutableHandleValue ret) = 0;
virtual void SendMUCMessage(const std::string& message) = 0;
};

View File

@ -321,10 +321,9 @@ void XmppClient::SendIqGetRatingList()
*
* @param data A JS array of game statistics
*/
void XmppClient::SendIqGameReport(ScriptInterface& scriptInterface, CScriptVal data)
void XmppClient::SendIqGameReport(ScriptInterface& scriptInterface, JS::HandleValue data)
{
glooxwrapper::JID xpartamuppJid(m_xpartamuppId);
JS::RootedValue dataval(scriptInterface.GetContext(), data.get());
// Setup some base stanza attributes
GameReport* game = new GameReport();
@ -332,11 +331,11 @@ void XmppClient::SendIqGameReport(ScriptInterface& scriptInterface, CScriptVal d
// Iterate through all the properties reported and add them to the stanza.
std::vector<std::string> properties;
scriptInterface.EnumeratePropertyNamesWithPrefix(dataval, "", properties);
scriptInterface.EnumeratePropertyNamesWithPrefix(data, "", properties);
for (std::vector<int>::size_type i = 0; i != properties.size(); i++)
{
std::wstring value;
scriptInterface.GetProperty(dataval, properties[i].c_str(), value);
scriptInterface.GetProperty(data, properties[i].c_str(), value);
report->addAttribute(properties[i], utf8_from_wstring(value));
}
@ -355,10 +354,9 @@ void XmppClient::SendIqGameReport(ScriptInterface& scriptInterface, CScriptVal d
*
* @param data A JS array of game attributes
*/
void XmppClient::SendIqRegisterGame(ScriptInterface& scriptInterface, CScriptVal data)
void XmppClient::SendIqRegisterGame(ScriptInterface& scriptInterface, JS::HandleValue data)
{
glooxwrapper::JID xpartamuppJid(m_xpartamuppId);
JS::RootedValue dataval(scriptInterface.GetContext(), data.get());
// Setup some base stanza attributes
GameListQuery* g = new GameListQuery();
@ -369,11 +367,11 @@ void XmppClient::SendIqRegisterGame(ScriptInterface& scriptInterface, CScriptVal
// Iterate through all the properties reported and add them to the stanza.
std::vector<std::string> properties;
scriptInterface.EnumeratePropertyNamesWithPrefix(dataval, "", properties);
scriptInterface.EnumeratePropertyNamesWithPrefix(data, "", properties);
for (std::vector<int>::size_type i = 0; i != properties.size(); i++)
{
std::wstring value;
scriptInterface.GetProperty(dataval, properties[i].c_str(), value);
scriptInterface.GetProperty(data, properties[i].c_str(), value);
game->addAttribute(properties[i], utf8_from_wstring(value));
}
@ -494,13 +492,12 @@ void XmppClient::handleOOB(const glooxwrapper::JID&, const glooxwrapper::OOB&)
*
* @return A JS array containing all known players and their presences
*/
CScriptValRooted XmppClient::GUIGetPlayerList(ScriptInterface& scriptInterface)
void XmppClient::GUIGetPlayerList(ScriptInterface& scriptInterface, JS::MutableHandleValue ret)
{
JSContext* cx = scriptInterface.GetContext();
JSAutoRequest rq(cx);
JS::RootedValue playerList(cx);
scriptInterface.Eval("([])", &playerList);
scriptInterface.Eval("([])", ret);
// Convert the internal data structure to a Javascript object.
for (std::map<std::string, std::vector<std::string> >::const_iterator it = m_PlayerMap.begin(); it != m_PlayerMap.end(); ++it)
@ -511,10 +508,8 @@ CScriptValRooted XmppClient::GUIGetPlayerList(ScriptInterface& scriptInterface)
scriptInterface.SetProperty(player, "presence", wstring_from_utf8(it->second[0]));
scriptInterface.SetProperty(player, "rating", wstring_from_utf8(it->second[1]));
scriptInterface.SetProperty(player, "role", wstring_from_utf8(it->second[2]));
scriptInterface.CallFunctionVoid(playerList, "push", player);
scriptInterface.CallFunctionVoid(ret, "push", player);
}
return CScriptValRooted(cx, playerList);
}
/**
@ -522,13 +517,12 @@ CScriptValRooted XmppClient::GUIGetPlayerList(ScriptInterface& scriptInterface)
*
* @return A JS array containing all known games
*/
CScriptValRooted XmppClient::GUIGetGameList(ScriptInterface& scriptInterface)
void XmppClient::GUIGetGameList(ScriptInterface& scriptInterface, JS::MutableHandleValue ret)
{
JSContext* cx = scriptInterface.GetContext();
JSAutoRequest rq(cx);
JS::RootedValue gameList(cx);
scriptInterface.Eval("([])", &gameList);
scriptInterface.Eval("([])", ret);
for(std::vector<const glooxwrapper::Tag*>::const_iterator it = m_GameList.begin(); it != m_GameList.end(); ++it)
{
JS::RootedValue game(cx);
@ -538,10 +532,8 @@ CScriptValRooted XmppClient::GUIGetGameList(ScriptInterface& scriptInterface)
for (size_t i = 0; i < ARRAY_SIZE(stats); ++i)
scriptInterface.SetProperty(game, stats[i], wstring_from_utf8((*it)->findAttribute(stats[i]).to_string()));
scriptInterface.CallFunctionVoid(gameList, "push", game);
scriptInterface.CallFunctionVoid(ret, "push", game);
}
return CScriptValRooted(cx, gameList);;
}
/**
@ -549,13 +541,12 @@ CScriptValRooted XmppClient::GUIGetGameList(ScriptInterface& scriptInterface)
*
* @return A JS array containing all known leaderboard data
*/
CScriptValRooted XmppClient::GUIGetBoardList(ScriptInterface& scriptInterface)
void XmppClient::GUIGetBoardList(ScriptInterface& scriptInterface, JS::MutableHandleValue ret)
{
JSContext* cx = scriptInterface.GetContext();
JSAutoRequest rq(cx);
JS::RootedValue boardList(cx);
scriptInterface.Eval("([])", &boardList);
scriptInterface.Eval("([])", ret);
for(std::vector<const glooxwrapper::Tag*>::const_iterator it = m_BoardList.begin(); it != m_BoardList.end(); ++it)
{
JS::RootedValue board(cx);
@ -565,10 +556,8 @@ CScriptValRooted XmppClient::GUIGetBoardList(ScriptInterface& scriptInterface)
for (size_t i = 0; i < ARRAY_SIZE(attributes); ++i)
scriptInterface.SetProperty(board, attributes[i], wstring_from_utf8((*it)->findAttribute(attributes[i]).to_string()));
scriptInterface.CallFunctionVoid(boardList, "push", board);
scriptInterface.CallFunctionVoid(ret, "push", board);
}
return CScriptValRooted(cx, boardList);
}
/**
@ -576,13 +565,12 @@ CScriptValRooted XmppClient::GUIGetBoardList(ScriptInterface& scriptInterface)
*
* @return A JS array containing the specific user's profile data
*/
CScriptValRooted XmppClient::GUIGetProfile(ScriptInterface& scriptInterface)
void XmppClient::GUIGetProfile(ScriptInterface& scriptInterface, JS::MutableHandleValue ret)
{
JSContext* cx = scriptInterface.GetContext();
JSAutoRequest rq(cx);
JS::RootedValue profileFetch(cx);
scriptInterface.Eval("([])", &profileFetch);
scriptInterface.Eval("([])", ret);
const char* stats[] = { "player", "rating", "totalGamesPlayed", "highestRating", "wins", "losses", "rank" };
for (std::vector<const glooxwrapper::Tag*>::const_iterator it = m_Profile.begin(); it != m_Profile.end(); ++it)
{
@ -592,10 +580,8 @@ CScriptValRooted XmppClient::GUIGetProfile(ScriptInterface& scriptInterface)
for (size_t i = 0; i < ARRAY_SIZE(stats); ++i)
scriptInterface.SetProperty(profile, stats[i], wstring_from_utf8((*it)->findAttribute(stats[i]).to_string()));
scriptInterface.CallFunctionVoid(profileFetch, "push", profile);
scriptInterface.CallFunctionVoid(ret, "push", profile);
}
return CScriptValRooted(cx, profileFetch);
}
/*****************************************************
@ -605,32 +591,32 @@ CScriptValRooted XmppClient::GUIGetProfile(ScriptInterface& scriptInterface)
/**
* Send GUI message queue when queried.
*/
CScriptValRooted XmppClient::GuiPollMessage(ScriptInterface& scriptInterface)
void XmppClient::GuiPollMessage(ScriptInterface& scriptInterface, JS::MutableHandleValue ret)
{
if (m_GuiMessageQueue.empty())
return CScriptValRooted();
{
ret.setUndefined();
return;
}
GUIMessage message = m_GuiMessageQueue.front();
JSContext* cx = scriptInterface.GetContext();
JSAutoRequest rq(cx);
JS::RootedValue messageVal(cx);
scriptInterface.Eval("({})", &messageVal);
scriptInterface.SetProperty(messageVal, "type", message.type);
scriptInterface.Eval("({})", ret);
scriptInterface.SetProperty(ret, "type", message.type);
if (!message.from.empty())
scriptInterface.SetProperty(messageVal, "from", message.from);
scriptInterface.SetProperty(ret, "from", message.from);
if (!message.text.empty())
scriptInterface.SetProperty(messageVal, "text", message.text);
scriptInterface.SetProperty(ret, "text", message.text);
if (!message.level.empty())
scriptInterface.SetProperty(messageVal, "level", message.level);
scriptInterface.SetProperty(ret, "level", message.level);
if (!message.message.empty())
scriptInterface.SetProperty(messageVal, "message", message.message);
scriptInterface.SetProperty(ret, "message", message.message);
if (!message.data.empty())
scriptInterface.SetProperty(messageVal, "data", message.data);
scriptInterface.SetProperty(ret, "data", message.data);
m_GuiMessageQueue.pop_front();
return CScriptValRooted(cx, messageVal);
}
/**

View File

@ -62,8 +62,8 @@ public:
void SendIqGetBoardList();
void SendIqGetRatingList();
void SendIqGetProfile(const std::string& player);
void SendIqGameReport(ScriptInterface& scriptInterface, CScriptVal data);
void SendIqRegisterGame(ScriptInterface& scriptInterface, CScriptVal data);
void SendIqGameReport(ScriptInterface& scriptInterface, JS::HandleValue data);
void SendIqRegisterGame(ScriptInterface& scriptInterface, JS::HandleValue data);
void SendIqUnregisterGame();
void SendIqChangeStateGame(const std::string& nbp, const std::string& players);
void SetNick(const std::string& nick);
@ -75,10 +75,10 @@ public:
void GetRole(const std::string& nickname, std::string& role);
void GetSubject(std::string& subject);
CScriptValRooted GUIGetPlayerList(ScriptInterface& scriptInterface);
CScriptValRooted GUIGetGameList(ScriptInterface& scriptInterface);
CScriptValRooted GUIGetBoardList(ScriptInterface& scriptInterface);
CScriptValRooted GUIGetProfile(ScriptInterface& scriptInterface);
void GUIGetPlayerList(ScriptInterface& scriptInterface, JS::MutableHandleValue ret);
void GUIGetGameList(ScriptInterface& scriptInterface, JS::MutableHandleValue ret);
void GUIGetBoardList(ScriptInterface& scriptInterface, JS::MutableHandleValue ret);
void GUIGetProfile(ScriptInterface& scriptInterface, JS::MutableHandleValue ret);
//Script
ScriptInterface& GetScriptInterface();
@ -132,7 +132,7 @@ public:
std::wstring from;
std::wstring message;
};
CScriptValRooted GuiPollMessage(ScriptInterface& scriptInterface);
void GuiPollMessage(ScriptInterface& scriptInterface, JS::MutableHandleValue ret);
void SendMUCMessage(const std::string& message);
protected:
void PushGuiMessage(XmppClient::GUIMessage message);

View File

@ -42,15 +42,15 @@ void JSI_Lobby::RegisterScriptFunctions(ScriptInterface& scriptInterface)
scriptInterface.RegisterFunction<void, &JSI_Lobby::SendGetBoardList>("SendGetBoardList");
scriptInterface.RegisterFunction<void, &JSI_Lobby::SendGetRatingList>("SendGetRatingList");
scriptInterface.RegisterFunction<void, std::wstring, &JSI_Lobby::SendGetProfile>("SendGetProfile");
scriptInterface.RegisterFunction<void, CScriptVal, &JSI_Lobby::SendRegisterGame>("SendRegisterGame");
scriptInterface.RegisterFunction<void, CScriptVal, &JSI_Lobby::SendGameReport>("SendGameReport");
scriptInterface.RegisterFunction<void, JS::HandleValue, &JSI_Lobby::SendRegisterGame>("SendRegisterGame");
scriptInterface.RegisterFunction<void, JS::HandleValue, &JSI_Lobby::SendGameReport>("SendGameReport");
scriptInterface.RegisterFunction<void, &JSI_Lobby::SendUnregisterGame>("SendUnregisterGame");
scriptInterface.RegisterFunction<void, std::wstring, std::wstring, &JSI_Lobby::SendChangeStateGame>("SendChangeStateGame");
scriptInterface.RegisterFunction<CScriptVal, &JSI_Lobby::GetPlayerList>("GetPlayerList");
scriptInterface.RegisterFunction<CScriptVal, &JSI_Lobby::GetGameList>("GetGameList");
scriptInterface.RegisterFunction<CScriptVal, &JSI_Lobby::GetBoardList>("GetBoardList");
scriptInterface.RegisterFunction<CScriptVal, &JSI_Lobby::GetProfile>("GetProfile");
scriptInterface.RegisterFunction<CScriptVal, &JSI_Lobby::LobbyGuiPollMessage>("LobbyGuiPollMessage");
scriptInterface.RegisterFunction<JS::Value, &JSI_Lobby::GetPlayerList>("GetPlayerList");
scriptInterface.RegisterFunction<JS::Value, &JSI_Lobby::GetGameList>("GetGameList");
scriptInterface.RegisterFunction<JS::Value, &JSI_Lobby::GetBoardList>("GetBoardList");
scriptInterface.RegisterFunction<JS::Value, &JSI_Lobby::GetProfile>("GetProfile");
scriptInterface.RegisterFunction<JS::Value, &JSI_Lobby::LobbyGuiPollMessage>("LobbyGuiPollMessage");
scriptInterface.RegisterFunction<void, std::wstring, &JSI_Lobby::LobbySendMessage>("LobbySendMessage");
scriptInterface.RegisterFunction<void, std::wstring, &JSI_Lobby::LobbySetPlayerPresence>("LobbySetPlayerPresence");
scriptInterface.RegisterFunction<void, std::wstring, &JSI_Lobby::LobbySetNick>("LobbySetNick");
@ -145,7 +145,7 @@ void JSI_Lobby::SendGetProfile(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), s
g_XmppClient->SendIqGetProfile(utf8_from_wstring(player));
}
void JSI_Lobby::SendGameReport(ScriptInterface::CxPrivate* pCxPrivate, CScriptVal data)
void JSI_Lobby::SendGameReport(ScriptInterface::CxPrivate* pCxPrivate, JS::HandleValue data)
{
if (!g_XmppClient)
return;
@ -153,7 +153,7 @@ void JSI_Lobby::SendGameReport(ScriptInterface::CxPrivate* pCxPrivate, CScriptVa
g_XmppClient->SendIqGameReport(*(pCxPrivate->pScriptInterface), data);
}
void JSI_Lobby::SendRegisterGame(ScriptInterface::CxPrivate* pCxPrivate, CScriptVal data)
void JSI_Lobby::SendRegisterGame(ScriptInterface::CxPrivate* pCxPrivate, JS::HandleValue data)
{
if (!g_XmppClient)
return;
@ -175,54 +175,74 @@ void JSI_Lobby::SendChangeStateGame(ScriptInterface::CxPrivate* UNUSED(pCxPrivat
g_XmppClient->SendIqChangeStateGame(utf8_from_wstring(nbp), utf8_from_wstring(players));
}
CScriptVal JSI_Lobby::GetPlayerList(ScriptInterface::CxPrivate* pCxPrivate)
JS::Value JSI_Lobby::GetPlayerList(ScriptInterface::CxPrivate* pCxPrivate)
{
if (!g_XmppClient)
return CScriptVal();
return JS::UndefinedValue();
JSContext* cx = pCxPrivate->pScriptInterface->GetContext();
JSAutoRequest rq(cx);
CScriptValRooted playerList = g_XmppClient->GUIGetPlayerList(*(pCxPrivate->pScriptInterface));
JS::RootedValue playerList(cx);
g_XmppClient->GUIGetPlayerList(*(pCxPrivate->pScriptInterface), &playerList);
return playerList.get();
return playerList;
}
CScriptVal JSI_Lobby::GetGameList(ScriptInterface::CxPrivate* pCxPrivate)
JS::Value JSI_Lobby::GetGameList(ScriptInterface::CxPrivate* pCxPrivate)
{
if (!g_XmppClient)
return CScriptVal();
return JS::UndefinedValue();
CScriptValRooted gameList = g_XmppClient->GUIGetGameList(*(pCxPrivate->pScriptInterface));
JSContext* cx = pCxPrivate->pScriptInterface->GetContext();
JSAutoRequest rq(cx);
return gameList.get();
JS::RootedValue gameList(cx);
g_XmppClient->GUIGetGameList(*(pCxPrivate->pScriptInterface), &gameList);
return gameList;
}
CScriptVal JSI_Lobby::GetBoardList(ScriptInterface::CxPrivate* pCxPrivate)
JS::Value JSI_Lobby::GetBoardList(ScriptInterface::CxPrivate* pCxPrivate)
{
if (!g_XmppClient)
return CScriptVal();
return JS::UndefinedValue();
CScriptValRooted boardList = g_XmppClient->GUIGetBoardList(*(pCxPrivate->pScriptInterface));
JSContext* cx = pCxPrivate->pScriptInterface->GetContext();
JSAutoRequest rq(cx);
return boardList.get();
JS::RootedValue boardList(cx);
g_XmppClient->GUIGetBoardList(*(pCxPrivate->pScriptInterface), &boardList);
return boardList;
}
CScriptVal JSI_Lobby::GetProfile(ScriptInterface::CxPrivate* pCxPrivate)
JS::Value JSI_Lobby::GetProfile(ScriptInterface::CxPrivate* pCxPrivate)
{
if (!g_XmppClient)
return CScriptVal();
return JS::UndefinedValue();
CScriptValRooted profileFetch = g_XmppClient->GUIGetProfile(*(pCxPrivate->pScriptInterface));
JSContext* cx = pCxPrivate->pScriptInterface->GetContext();
JSAutoRequest rq(cx);
return profileFetch.get();
JS::RootedValue profileFetch(cx);
g_XmppClient->GUIGetProfile(*(pCxPrivate->pScriptInterface), &profileFetch);
return profileFetch;
}
CScriptVal JSI_Lobby::LobbyGuiPollMessage(ScriptInterface::CxPrivate* pCxPrivate)
JS::Value JSI_Lobby::LobbyGuiPollMessage(ScriptInterface::CxPrivate* pCxPrivate)
{
if (!g_XmppClient)
return CScriptVal();
return JS::UndefinedValue();
JSContext* cx = pCxPrivate->pScriptInterface->GetContext();
JSAutoRequest rq(cx);
CScriptValRooted poll = g_XmppClient->GuiPollMessage(*(pCxPrivate->pScriptInterface));
JS::RootedValue poll(cx);
g_XmppClient->GuiPollMessage(*(pCxPrivate->pScriptInterface), &poll);
return poll.get();
return poll;
}
void JSI_Lobby::LobbySendMessage(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), std::wstring message)

View File

@ -40,15 +40,15 @@ namespace JSI_Lobby
void SendGetBoardList(ScriptInterface::CxPrivate* pCxPrivate);
void SendGetRatingList(ScriptInterface::CxPrivate* pCxPrivate);
void SendGetProfile(ScriptInterface::CxPrivate* pCxPrivate, std::wstring player);
void SendGameReport(ScriptInterface::CxPrivate* pCxPrivate, CScriptVal data);
void SendRegisterGame(ScriptInterface::CxPrivate* pCxPrivate, CScriptVal data);
void SendGameReport(ScriptInterface::CxPrivate* pCxPrivate, JS::HandleValue data);
void SendRegisterGame(ScriptInterface::CxPrivate* pCxPrivate, JS::HandleValue data);
void SendUnregisterGame(ScriptInterface::CxPrivate* pCxPrivate);
void SendChangeStateGame(ScriptInterface::CxPrivate* pCxPrivate, std::wstring nbp, std::wstring players);
CScriptVal GetPlayerList(ScriptInterface::CxPrivate* pCxPrivate);
CScriptVal GetGameList(ScriptInterface::CxPrivate* pCxPrivate);
CScriptVal GetBoardList(ScriptInterface::CxPrivate* pCxPrivate);
CScriptVal GetProfile(ScriptInterface::CxPrivate* pCxPrivate);
CScriptVal LobbyGuiPollMessage(ScriptInterface::CxPrivate* pCxPrivate);
JS::Value GetPlayerList(ScriptInterface::CxPrivate* pCxPrivate);
JS::Value GetGameList(ScriptInterface::CxPrivate* pCxPrivate);
JS::Value GetBoardList(ScriptInterface::CxPrivate* pCxPrivate);
JS::Value GetProfile(ScriptInterface::CxPrivate* pCxPrivate);
JS::Value LobbyGuiPollMessage(ScriptInterface::CxPrivate* pCxPrivate);
void LobbySendMessage(ScriptInterface::CxPrivate* pCxPrivate, std::wstring message);
void LobbySetPlayerPresence(ScriptInterface::CxPrivate* pCxPrivate, std::wstring presence);
void LobbySetNick(ScriptInterface::CxPrivate* pCxPrivate, std::wstring nick);

View File

@ -69,7 +69,8 @@ private:
CNetClient::CNetClient(CGame* game) :
m_Session(NULL),
m_UserName(L"anonymous"),
m_GUID(ps_generate_guid()), m_HostID((u32)-1), m_ClientTurnManager(NULL), m_Game(game)
m_GUID(ps_generate_guid()), m_HostID((u32)-1), m_ClientTurnManager(NULL), m_Game(game),
m_GameAttributes(game->GetSimulation2()->GetScriptInterface().GetContext())
{
m_Game->SetTurnManager(NULL); // delete the old local turn manager so we don't accidentally use it
@ -544,7 +545,7 @@ bool CNetClient::OnGameStart(void* context, CFsmEvent* event)
*client->m_Game->GetSimulation2(), *client, client->m_HostID, client->m_Game->GetReplayLogger());
client->m_Game->SetPlayerID(player);
client->m_Game->StartGame(client->m_GameAttributes, "");
client->m_Game->StartGame(&client->m_GameAttributes, "");
JS::RootedValue msg(cx);
client->GetScriptInterface().Eval("({'type':'start'})", &msg);

View File

@ -219,7 +219,7 @@ private:
u32 m_HostID;
/// Latest copy of game setup attributes heard from the server
CScriptValRooted m_GameAttributes;
JS::PersistentRootedValue m_GameAttributes;
/// Latest copy of player assignments heard from the server
PlayerAssignmentMap m_PlayerAssignments;

View File

@ -114,7 +114,14 @@ class CSimulationMessage : public CNetMessage
{
public:
CSimulationMessage(ScriptInterface& scriptInterface);
CSimulationMessage(ScriptInterface& scriptInterface, u32 client, i32 player, u32 turn, jsval data);
CSimulationMessage(ScriptInterface& scriptInterface, u32 client, i32 player, u32 turn, JS::HandleValue data);
/** The compiler can't create a copy constructor because of the PersistentRooted member,
* so we have to write it manually.
* NOTE: It doesn't clone the m_Data member and the copy will reference the same JS::Value!
*/
CSimulationMessage(const CSimulationMessage& orig);
virtual u8* Serialize(u8* pBuffer) const;
virtual const u8* Deserialize(const u8* pStart, const u8* pEnd);
virtual size_t GetSerializedLength() const;
@ -123,7 +130,7 @@ public:
u32 m_Client;
i32 m_Player;
u32 m_Turn;
CScriptValRooted m_Data;
JS::PersistentRooted<JS::Value> m_Data;
private:
ScriptInterface* m_ScriptInterface;
};
@ -136,13 +143,13 @@ class CGameSetupMessage : public CNetMessage
NONCOPYABLE(CGameSetupMessage);
public:
CGameSetupMessage(ScriptInterface& scriptInterface);
CGameSetupMessage(ScriptInterface& scriptInterface, jsval data);
CGameSetupMessage(ScriptInterface& scriptInterface, JS::HandleValue data);
virtual u8* Serialize(u8* pBuffer) const;
virtual const u8* Deserialize(const u8* pStart, const u8* pEnd);
virtual size_t GetSerializedLength() const;
virtual CStr ToString() const;
CScriptValRooted m_Data;
JS::PersistentRootedValue m_Data;
private:
ScriptInterface& m_ScriptInterface;
};

View File

@ -110,30 +110,38 @@ public:
};
CSimulationMessage::CSimulationMessage(ScriptInterface& scriptInterface) :
CNetMessage(NMT_SIMULATION_COMMAND), m_ScriptInterface(&scriptInterface)
CNetMessage(NMT_SIMULATION_COMMAND), m_ScriptInterface(&scriptInterface), m_Data(scriptInterface.GetJSRuntime())
{
}
CSimulationMessage::CSimulationMessage(ScriptInterface& scriptInterface, u32 client, i32 player, u32 turn, jsval data) :
CSimulationMessage::CSimulationMessage(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.GetContext(), data)
m_Client(client), m_Player(player), m_Turn(turn), m_Data(scriptInterface.GetJSRuntime(), data)
{
}
CSimulationMessage::CSimulationMessage(const CSimulationMessage& orig) :
m_Data(orig.m_ScriptInterface->GetJSRuntime()),
m_Client(orig.m_Client),
m_Player(orig.m_Player),
m_ScriptInterface(orig.m_ScriptInterface),
m_Turn(orig.m_Turn),
CNetMessage(orig)
{
m_Data.set(orig.m_Data);
}
u8* CSimulationMessage::Serialize(u8* pBuffer) const
{
// TODO: ought to handle serialization exceptions
// TODO: ought to represent common commands more efficiently
JSContext* cx = m_ScriptInterface->GetContext();
JSAutoRequest rq(cx);
JS::RootedValue tmpData(cx, m_Data.get()); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
u8* pos = CNetMessage::Serialize(pBuffer);
CBufferBinarySerializer serializer(*m_ScriptInterface, pos);
serializer.NumberU32_Unbounded("client", m_Client);
serializer.NumberI32_Unbounded("player", m_Player);
serializer.NumberU32_Unbounded("turn", m_Turn);
serializer.ScriptVal("command", &tmpData);
serializer.ScriptVal("command", const_cast<JS::PersistentRootedValue*>(&m_Data));
return serializer.GetBuffer();
}
@ -141,18 +149,13 @@ const u8* CSimulationMessage::Deserialize(const u8* pStart, const u8* pEnd)
{
// TODO: ought to handle serialization exceptions
// TODO: ought to represent common commands more efficiently
JSContext* cx = m_ScriptInterface->GetContext();
JSAutoRequest rq(cx);
JS::RootedValue tmpData(cx); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
const u8* pos = CNetMessage::Deserialize(pStart, pEnd);
std::istringstream stream(std::string(pos, pEnd));
CStdDeserializer deserializer(*m_ScriptInterface, stream);
deserializer.NumberU32_Unbounded("client", m_Client);
deserializer.NumberI32_Unbounded("player", m_Player);
deserializer.NumberU32_Unbounded("turn", m_Turn);
deserializer.ScriptVal("command", &tmpData);
m_Data = CScriptValRooted(cx, tmpData);
deserializer.ScriptVal("command", &m_Data);
return pEnd;
}
@ -160,24 +163,20 @@ size_t CSimulationMessage::GetSerializedLength() const
{
// TODO: serializing twice is stupidly inefficient - we should just
// do it once, store the result, and use it here and in Serialize
JSContext* cx = m_ScriptInterface->GetContext();
JSAutoRequest rq(cx);
JS::RootedValue tmpData(cx, m_Data.get()); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
CLengthBinarySerializer serializer(*m_ScriptInterface);
serializer.NumberU32_Unbounded("client", m_Client);
serializer.NumberI32_Unbounded("player", m_Player);
serializer.NumberU32_Unbounded("turn", m_Turn);
serializer.ScriptVal("command", &tmpData);
// TODO: The cast can probably be removed if and when ScriptVal can take a JS::HandleValue instead of
// a JS::MutableHandleValue (relies on JSAPI change). Also search for other casts like this one in that case.
serializer.ScriptVal("command", const_cast<JS::PersistentRootedValue*>(&m_Data));
return CNetMessage::GetSerializedLength() + serializer.GetLength();
}
CStr CSimulationMessage::ToString() const
{
JSContext* cx = m_ScriptInterface->GetContext();
JSAutoRequest rq(cx);
JS::RootedValue tmpData(cx, m_Data.get()); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
std::string source = utf8_from_wstring(m_ScriptInterface->ToString(&tmpData));
std::string source = utf8_from_wstring(m_ScriptInterface->ToString(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 << " }";
@ -186,61 +185,45 @@ CStr CSimulationMessage::ToString() const
CGameSetupMessage::CGameSetupMessage(ScriptInterface& scriptInterface) :
CNetMessage(NMT_GAME_SETUP), m_ScriptInterface(scriptInterface)
CNetMessage(NMT_GAME_SETUP), m_ScriptInterface(scriptInterface), m_Data(scriptInterface.GetJSRuntime())
{
}
CGameSetupMessage::CGameSetupMessage(ScriptInterface& scriptInterface, jsval data) :
CGameSetupMessage::CGameSetupMessage(ScriptInterface& scriptInterface, JS::HandleValue data) :
CNetMessage(NMT_GAME_SETUP), m_ScriptInterface(scriptInterface),
m_Data(scriptInterface.GetContext(), data)
m_Data(scriptInterface.GetJSRuntime(), data)
{
}
u8* CGameSetupMessage::Serialize(u8* pBuffer) const
{
// TODO: ought to handle serialization exceptions
JSContext* cx = m_ScriptInterface.GetContext();
JSAutoRequest rq(cx);
JS::RootedValue tmpData(cx, m_Data.get()); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
u8* pos = CNetMessage::Serialize(pBuffer);
CBufferBinarySerializer serializer(m_ScriptInterface, pos);
serializer.ScriptVal("command", &tmpData);
serializer.ScriptVal("command", const_cast<JS::PersistentRootedValue*>(&m_Data));
return serializer.GetBuffer();
}
const u8* CGameSetupMessage::Deserialize(const u8* pStart, const u8* pEnd)
{
// TODO: ought to handle serialization exceptions
JSContext* cx = m_ScriptInterface.GetContext();
JSAutoRequest rq(cx);
JS::RootedValue tmpData(cx); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
const u8* pos = CNetMessage::Deserialize(pStart, pEnd);
std::istringstream stream(std::string(pos, pEnd));
CStdDeserializer deserializer(m_ScriptInterface, stream);
deserializer.ScriptVal("command", &tmpData);
m_Data = CScriptValRooted(cx, tmpData);
deserializer.ScriptVal("command", const_cast<JS::PersistentRootedValue*>(&m_Data));
return pEnd;
}
size_t CGameSetupMessage::GetSerializedLength() const
{
JSContext* cx = m_ScriptInterface.GetContext();
JSAutoRequest rq(cx);
JS::RootedValue tmpData(cx, m_Data.get()); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
CLengthBinarySerializer serializer(m_ScriptInterface);
serializer.ScriptVal("command", &tmpData);
serializer.ScriptVal("command", const_cast<JS::PersistentRootedValue*>(&m_Data));
return CNetMessage::GetSerializedLength() + serializer.GetLength();
}
CStr CGameSetupMessage::ToString() const
{
JSContext* cx = m_ScriptInterface.GetContext();
JSAutoRequest rq(cx);
JS::RootedValue tmpData(cx, m_Data.get()); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
std::string source = utf8_from_wstring(m_ScriptInterface.ToString(&tmpData));
std::string source = utf8_from_wstring(m_ScriptInterface.ToString(const_cast<JS::PersistentRootedValue*>(&m_Data)));
std::stringstream stream;
stream << "CGameSetupMessage { m_Data: " << source << " }";

View File

@ -365,7 +365,9 @@ void CNetServerWorker::Run()
// To avoid the need for JS_SetContextThread, we create and use and destroy
// the script interface entirely within this network thread
m_ScriptInterface = new ScriptInterface("Engine", "Net server", ScriptInterface::CreateRuntime());
m_ScriptInterface = new ScriptInterface("Engine", "Net server", ScriptInterface::CreateRuntime(g_ScriptRuntime));
m_GameAttributes.set(m_ScriptInterface->GetJSRuntime(), JS::UndefinedValue());
while (true)
{
if (!RunStep())
@ -378,9 +380,9 @@ void CNetServerWorker::Run()
// Update profiler stats
m_Stats->LatchHostState(m_Host);
}
// Clear roots before deleting their context
m_GameAttributes = CScriptValRooted();
m_GameAttributes.clear();
m_SavedCommands.clear();
SAFE_DELETE(m_ScriptInterface);
@ -615,7 +617,7 @@ void CNetServerWorker::OnUserJoin(CNetServerSession* session)
AddPlayer(session->GetGUID(), session->GetUserName());
CGameSetupMessage gameSetupMessage(GetScriptInterface());
gameSetupMessage.m_Data = m_GameAttributes;
gameSetupMessage.m_Data = m_GameAttributes.get();
session->SendMessage(&gameSetupMessage);
CPlayerAssignmentMessage assignMessage;
@ -1022,12 +1024,8 @@ void CNetServerWorker::StartGame()
m_State = SERVER_STATE_LOADING;
JSContext* cx = GetScriptInterface().GetContext();
JSAutoRequest rq(cx);
// TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
JS::RootedValue tmpGameAttributes(cx, m_GameAttributes.get());
// Send the final setup state to all clients
UpdateGameAttributes(&tmpGameAttributes);
UpdateGameAttributes(&m_GameAttributes.get());
SendPlayerAssignments();
CGameStartMessage gameStart;
@ -1036,13 +1034,13 @@ void CNetServerWorker::StartGame()
void CNetServerWorker::UpdateGameAttributes(JS::MutableHandleValue attrs)
{
m_GameAttributes = CScriptValRooted(GetScriptInterface().GetContext(), attrs);
m_GameAttributes.set(m_ScriptInterface->GetJSRuntime(), attrs);
if (!m_Host)
return;
CGameSetupMessage gameSetupMessage(GetScriptInterface());
gameSetupMessage.m_Data = m_GameAttributes;
gameSetupMessage.m_Data.set(m_GameAttributes.get());
Broadcast(&gameSetupMessage);
}

View File

@ -281,7 +281,10 @@ private:
PlayerAssignmentMap m_PlayerAssignments;
CScriptValRooted m_GameAttributes;
/**
* Stores the most current game attributes.
*/
DefPersistentRooted<JS::Value> m_GameAttributes;
int m_AutostartPlayers;

View File

@ -159,7 +159,7 @@ bool CNetTurnManager::Update(float simFrameLength, size_t maxTurns)
std::vector<SimulationCommand> commands;
for (std::map<u32, std::vector<SimulationCommand> >::iterator it = m_QueuedCommands[0].begin(); it != m_QueuedCommands[0].end(); ++it)
{
commands.insert(commands.end(), it->second.begin(), it->second.end());
commands.insert(commands.end(), std::make_move_iterator(it->second.begin()), std::make_move_iterator(it->second.end()));
}
m_QueuedCommands.pop_front();
m_QueuedCommands.resize(m_QueuedCommands.size() + 1);
@ -203,7 +203,7 @@ bool CNetTurnManager::UpdateFastForward()
std::vector<SimulationCommand> commands;
for (std::map<u32, std::vector<SimulationCommand> >::iterator it = m_QueuedCommands[0].begin(); it != m_QueuedCommands[0].end(); ++it)
{
commands.insert(commands.end(), it->second.begin(), it->second.end());
commands.insert(commands.end(), std::make_move_iterator(it->second.begin()), std::make_move_iterator(it->second.end()));
}
m_QueuedCommands.pop_front();
m_QueuedCommands.resize(m_QueuedCommands.size() + 1);
@ -256,7 +256,7 @@ void CNetTurnManager::Interpolate(float simFrameLength, float realFrameLength)
m_Simulation2.Interpolate(simFrameLength, offset, realFrameLength);
}
void CNetTurnManager::AddCommand(int client, int player, CScriptValRooted data, u32 turn)
void CNetTurnManager::AddCommand(int client, int player, JS::HandleValue data, u32 turn)
{
NETTURN_LOG((L"AddCommand(client=%d player=%d turn=%d)\n", client, player, turn));
@ -266,10 +266,8 @@ void CNetTurnManager::AddCommand(int client, int player, CScriptValRooted data,
return;
}
SimulationCommand cmd;
cmd.player = player;
cmd.data = data;
m_QueuedCommands[turn - (m_CurrentTurn+1)][client].push_back(cmd);
SimulationCommand cmd(player, m_Simulation2.GetScriptInterface().GetContext(), data);
m_QueuedCommands[turn - (m_CurrentTurn+1)][client].emplace_back(std::move(cmd));
}
void CNetTurnManager::FinishedAllCommands(u32 turn, u32 turnLength)
@ -374,12 +372,12 @@ CNetClientTurnManager::CNetClientTurnManager(CSimulation2& simulation, CNetClien
{
}
void CNetClientTurnManager::PostCommand(CScriptValRooted data)
void CNetClientTurnManager::PostCommand(JS::HandleValue data)
{
NETTURN_LOG((L"PostCommand()\n"));
// Transmit command to server
CSimulationMessage msg(m_Simulation2.GetScriptInterface(), m_ClientId, m_PlayerId, m_CurrentTurn + COMMAND_DELAY, data.get());
CSimulationMessage msg(m_Simulation2.GetScriptInterface(), m_ClientId, m_PlayerId, m_CurrentTurn + COMMAND_DELAY, data);
m_NetClient.SendMessage(&msg);
// Add to our local queue
@ -436,7 +434,7 @@ CNetLocalTurnManager::CNetLocalTurnManager(CSimulation2& simulation, IReplayLogg
{
}
void CNetLocalTurnManager::PostCommand(CScriptValRooted data)
void CNetLocalTurnManager::PostCommand(JS::HandleValue data)
{
// Add directly to the next turn, ignoring COMMAND_DELAY,
// because we don't need to compensate for network latency

View File

@ -110,7 +110,7 @@ public:
/**
* Called by simulation code, to add a new command to be distributed to all clients and executed soon.
*/
virtual void PostCommand(CScriptValRooted data) = 0;
virtual void PostCommand(JS::HandleValue data) = 0;
/**
* Called when all commands for a given turn have been received.
@ -139,7 +139,7 @@ protected:
/**
* Store a command to be executed at a given turn.
*/
void AddCommand(int client, int player, CScriptValRooted data, u32 turn);
void AddCommand(int client, int player, JS::HandleValue data, u32 turn);
/**
* Called when this client has finished sending all its commands scheduled for the given turn.
@ -199,7 +199,7 @@ public:
virtual void OnSimulationMessage(CSimulationMessage* msg);
virtual void PostCommand(CScriptValRooted data);
virtual void PostCommand(JS::HandleValue data);
/**
* Notifiy the server that all commands are sent to prepare the connection for termination.
@ -224,7 +224,7 @@ public:
virtual void OnSimulationMessage(CSimulationMessage* msg);
virtual void PostCommand(CScriptValRooted data);
virtual void PostCommand(JS::HandleValue data);
protected:
virtual void NotifyFinishedOwnCommands(u32 turn);

View File

@ -175,14 +175,14 @@ public:
wait(clients, 100);
{
CScriptValRooted cmd;
client1.GetScriptInterface().Eval("({type:'debug-print', message:'[>>> client1 test sim command]\\n'})", cmd);
JS::RootedValue cmd(cx);
client1.GetScriptInterface().Eval("({type:'debug-print', message:'[>>> client1 test sim command]\\n'})", &cmd);
client1Game.GetTurnManager()->PostCommand(cmd);
}
{
CScriptValRooted cmd;
client2.GetScriptInterface().Eval("({type:'debug-print', message:'[>>> client2 test sim command]\\n'})", cmd);
JS::RootedValue cmd(cx);
client2.GetScriptInterface().Eval("({type:'debug-print', message:'[>>> client2 test sim command]\\n'})", &cmd);
client2Game.GetTurnManager()->PostCommand(cmd);
}
@ -244,8 +244,8 @@ public:
wait(clients, 100);
{
CScriptValRooted cmd;
client1.GetScriptInterface().Eval("({type:'debug-print', message:'[>>> client1 test sim command 1]\\n'})", cmd);
JS::RootedValue cmd(cx);
client1.GetScriptInterface().Eval("({type:'debug-print', message:'[>>> client1 test sim command 1]\\n'})", &cmd);
client1Game.GetTurnManager()->PostCommand(cmd);
}
@ -256,8 +256,8 @@ public:
wait(clients, 100);
{
CScriptValRooted cmd;
client1.GetScriptInterface().Eval("({type:'debug-print', message:'[>>> client1 test sim command 2]\\n'})", cmd);
JS::RootedValue cmd(cx);
client1.GetScriptInterface().Eval("({type:'debug-print', message:'[>>> client1 test sim command 2]\\n'})", &cmd);
client1Game.GetTurnManager()->PostCommand(cmd);
}
@ -311,8 +311,8 @@ public:
// CNetTurnManager::TurnNeedsFullHash to always return true)
{
CScriptValRooted cmd;
client1.GetScriptInterface().Eval("({type:'debug-print', message:'[>>> client1 test sim command 3]\\n'})", cmd);
JS::RootedValue cmd(cx);
client1.GetScriptInterface().Eval("({type:'debug-print', message:'[>>> client1 test sim command 3]\\n'})", &cmd);
client1Game.GetTurnManager()->PostCommand(cmd);
}
@ -324,8 +324,8 @@ public:
wait(clients, 100);
{
CScriptValRooted cmd;
client1.GetScriptInterface().Eval("({type:'debug-print', message:'[>>> client1 test sim command 4]\\n'})", cmd);
JS::RootedValue cmd(cx);
client1.GetScriptInterface().Eval("({type:'debug-print', message:'[>>> client1 test sim command 4]\\n'})", &cmd);
client1Game.GetTurnManager()->PostCommand(cmd);
}

View File

@ -122,20 +122,20 @@ void CGame::SetTurnManager(CNetTurnManager* turnManager)
**/
void CGame::RegisterInit(const JS::HandleValue attribs, const std::string& savedState)
{
JSContext* cx = m_Simulation2->GetScriptInterface().GetContext();
ScriptInterface& scriptInterface = m_Simulation2->GetScriptInterface();
JSContext* cx = scriptInterface.GetContext();
JSAutoRequest rq(cx);
m_InitialSavedState = savedState;
m_IsSavedGame = !savedState.empty();
CScriptValRooted tmpAttribs(cx, attribs);
m_Simulation2->SetInitAttributes(tmpAttribs);
m_Simulation2->SetInitAttributes(attribs);
std::string mapType;
m_Simulation2->GetScriptInterface().GetProperty(attribs, "mapType", mapType);
scriptInterface.GetProperty(attribs, "mapType", mapType);
float speed;
if (m_Simulation2->GetScriptInterface().HasProperty(attribs, "gameSpeed") && m_Simulation2->GetScriptInterface().GetProperty(attribs, "gameSpeed", speed))
if (scriptInterface.HasProperty(attribs, "gameSpeed") && scriptInterface.GetProperty(attribs, "gameSpeed", speed))
SetSimRate(speed);
LDR_BeginRegistering();
@ -154,21 +154,21 @@ void CGame::RegisterInit(const JS::HandleValue attribs, const std::string& saved
{
// Load random map attributes
std::wstring scriptFile;
CScriptValRooted settings;
JS::RootedValue settings(cx);
m_Simulation2->GetScriptInterface().GetProperty(attribs, "script", scriptFile);
m_Simulation2->GetScriptInterface().GetProperty(attribs, "settings", settings);
scriptInterface.GetProperty(attribs, "script", scriptFile);
scriptInterface.GetProperty(attribs, "settings", &settings);
m_World->RegisterInitRMS(scriptFile, settings, m_PlayerID);
m_World->RegisterInitRMS(scriptFile, scriptInterface.GetJSRuntime(), settings, m_PlayerID);
}
else
{
std::wstring mapFile;
m_Simulation2->GetScriptInterface().GetProperty(attribs, "map", mapFile);
CScriptValRooted settings;
m_Simulation2->GetScriptInterface().GetProperty(attribs, "settings", settings);
JS::RootedValue settings(cx);
scriptInterface.GetProperty(attribs, "map", mapFile);
scriptInterface.GetProperty(attribs, "settings", &settings);
m_World->RegisterInit(mapFile, settings, m_PlayerID);
m_World->RegisterInit(mapFile, scriptInterface.GetJSRuntime(), settings, m_PlayerID);
}
if (m_GameView)
RegMemFun(g_Renderer.GetSingletonPtr()->GetWaterManager(), &WaterManager::LoadWaterTextures, L"LoadWaterTextures", 80);
@ -218,9 +218,9 @@ PSRETURN CGame::ReallyStartGame()
m_Simulation2->PreInitGame();
JS::RootedValue settings(cx);
JS::RootedValue tmpInitAttributes(cx, m_Simulation2->GetInitAttributes().get());
JS::RootedValue tmpInitAttributes(cx, m_Simulation2->GetInitAttributes());
m_Simulation2->GetScriptInterface().GetProperty(tmpInitAttributes, "settings", &settings);
m_Simulation2->InitGame(CScriptVal(settings));
m_Simulation2->InitGame(settings);
}
// We need to do an initial Interpolate call to set up all the models etc,
@ -270,14 +270,9 @@ void CGame::SetPlayerID(int playerID)
m_TurnManager->SetPlayerID(m_PlayerID);
}
void CGame::StartGame(const CScriptValRooted& attribs1, const std::string& savedState)
void CGame::StartGame(JS::MutableHandleValue attribs, const std::string& savedState)
{
JSContext* cx = m_Simulation2->GetScriptInterface().GetContext();
JSAutoRequest rq(cx);
JS::RootedValue attribs(cx, attribs1.get()); // TODO: Get Handle parameter directly with SpiderMonkey 31
m_ReplayLogger->StartGame(&attribs);
m_ReplayLogger->StartGame(attribs);
RegisterInit(attribs, savedState);
}

View File

@ -73,7 +73,7 @@ public:
**/
bool m_Paused;
void StartGame(const CScriptValRooted& attribs, const std::string& savedState);
void StartGame(JS::MutableHandleValue attribs, const std::string& savedState);
PSRETURN ReallyStartGame();
/**

View File

@ -886,12 +886,26 @@ bool Init(const CmdLineArgs& args, int flags)
// This must come after VFS init, which sets the current directory
// (required for finding our output log files).
g_Logger = new CLogger;
new CProfileViewer;
new CProfileManager; // before any script code
g_ScriptStatsTable = new CScriptStatsTable;
g_ProfileViewer.AddRootTable(g_ScriptStatsTable);
// Set up the console early, so that debugging
// messages can be logged to it. (The console's size
// and fonts are set later in InitPs())
g_Console = new CConsole();
// g_ConfigDB, command line args, globals
CONFIG_Init(args);
// Using a global object for the runtime is a workaround until Simulation and AI use
// their own threads and also their own runtimes.
const int runtimeSize = 384 * 1024 * 1024;
const int heapGrowthBytesGCTrigger = 20 * 1024 * 1024;
g_ScriptRuntime = ScriptInterface::CreateRuntime(runtimeSize, heapGrowthBytesGCTrigger);
g_ScriptRuntime = ScriptInterface::CreateRuntime(shared_ptr<ScriptRuntime>(), runtimeSize, heapGrowthBytesGCTrigger);
// Special command-line mode to dump the entity schemas instead of running the game.
// (This must be done after loading VFS etc, but should be done before wasting time
@ -912,26 +926,12 @@ bool Init(const CmdLineArgs& args, int flags)
hooks.translate_free = psTranslateFree;
app_hooks_update(&hooks);
// Set up the console early, so that debugging
// messages can be logged to it. (The console's size
// and fonts are set later in InitPs())
g_Console = new CConsole();
CNetHost::Initialize();
new CProfileViewer;
new CProfileManager; // before any script code
g_ScriptStatsTable = new CScriptStatsTable;
g_ProfileViewer.AddRootTable(g_ScriptStatsTable);
#if CONFIG2_AUDIO
ISoundManager::CreateSoundManager();
#endif
// g_ConfigDB, command line args, globals
CONFIG_Init(args);
// Check if there are mods specified on the command line,
// or if we already set the mods (~INIT_MODS),
// else check if there are mods that should be loaded specified
@ -1453,7 +1453,7 @@ bool Autostart(const CmdLineArgs& args)
else
{
g_Game->SetPlayerID(1);
g_Game->StartGame(CScriptValRooted(cx, attrs), "");
g_Game->StartGame(&attrs, "");
LDR_NonprogressiveLoad();

View File

@ -69,13 +69,12 @@
static void ReportGLLimits(ScriptInterface& scriptInterface, JS::HandleValue settings);
#if ARCH_X86_X64
CScriptVal ConvertCaches(ScriptInterface& scriptInterface, x86_x64::IdxCache idxCache)
void ConvertCaches(ScriptInterface& scriptInterface, x86_x64::IdxCache idxCache, JS::MutableHandleValue ret)
{
JSContext* cx = scriptInterface.GetContext();
JSAutoRequest rq(cx);
JS::RootedValue ret(cx);
scriptInterface.Eval("[]", &ret);
scriptInterface.Eval("[]", ret);
for (size_t idxLevel = 0; idxLevel < x86_x64::Cache::maxLevels; ++idxLevel)
{
const x86_x64::Cache* pcache = x86_x64::Caches(idxCache+idxLevel);
@ -91,16 +90,14 @@ CScriptVal ConvertCaches(ScriptInterface& scriptInterface, x86_x64::IdxCache idx
scriptInterface.SetProperty(cache, "totalsize", (u32)pcache->TotalSize());
scriptInterface.SetPropertyInt(ret, idxLevel, cache);
}
return ret.get();
}
CScriptVal ConvertTLBs(ScriptInterface& scriptInterface)
void ConvertTLBs(ScriptInterface& scriptInterface, JS::MutableHandleValue ret)
{
JSContext* cx = scriptInterface.GetContext();
JSAutoRequest rq(cx);
JS::RootedValue ret(cx);
scriptInterface.Eval("[]", &ret);
scriptInterface.Eval("[]", ret);
for(size_t i = 0; ; i++)
{
const x86_x64::Cache* ptlb = x86_x64::Caches(x86_x64::TLB+i);
@ -115,7 +112,6 @@ CScriptVal ConvertTLBs(ScriptInterface& scriptInterface)
scriptInterface.SetProperty(tlb, "entries", (u32)ptlb->numEntries);
scriptInterface.SetPropertyInt(ret, i, tlb);
}
return ret.get();
}
#endif
@ -312,9 +308,13 @@ void RunHardwareDetection()
scriptInterface.SetProperty(settings, "x86_caps[2]", caps2);
scriptInterface.SetProperty(settings, "x86_caps[3]", caps3);
scriptInterface.SetProperty(settings, "x86_icaches", ConvertCaches(scriptInterface, x86_x64::L1I));
scriptInterface.SetProperty(settings, "x86_dcaches", ConvertCaches(scriptInterface, x86_x64::L1D));
scriptInterface.SetProperty(settings, "x86_tlbs", ConvertTLBs(scriptInterface));
JS::RootedValue tmpVal(cx);
ConvertCaches(scriptInterface, x86_x64::L1I, &tmpVal);
scriptInterface.SetProperty(settings, "x86_icaches", tmpVal);
ConvertCaches(scriptInterface, x86_x64::L1D, &tmpVal);
scriptInterface.SetProperty(settings, "x86_dcaches", tmpVal);
ConvertTLBs(scriptInterface, &tmpVal);
scriptInterface.SetProperty(settings, "x86_tlbs", tmpVal);
#endif
scriptInterface.SetProperty(settings, "timer_resolution", timer_Resolution());

View File

@ -29,6 +29,11 @@
#include "ps/Singleton.h"
#include "ps/ThreadUtil.h"
#include <boost/flyweight.hpp>
#include <boost/flyweight/key_value.hpp>
#include <boost/flyweight/no_locking.hpp>
#include <boost/flyweight/no_tracking.hpp>
#define PROFILE_AMORTIZE_FRAMES 30
#define PROFILE_AMORTIZE_TURNS 1
@ -38,6 +43,25 @@ class CProfileNodeTable;
class CStr8;
class CStrW;
// To profile scripts usefully, we use a call hook that's called on every enter/exit,
// and need to find the function name. But most functions are anonymous so we make do
// with filename plus line number instead.
// Computing the names is fairly expensive, and we need to return an interned char*
// for the profiler to hold a copy of, so we use boost::flyweight to construct interned
// strings per call location.
//
// TODO: Check again how much the overhead for getting filename and line really is and if
// it has increased with the new approach after the SpiderMonkey 31 upgrade.
//
// Flyweight types (with no_locking because the call hooks are only used in the
// main thread, and no_tracking because we mustn't delete values the profiler is
// using and it's not going to waste much memory)
typedef boost::flyweight<
std::string,
boost::flyweights::no_tracking,
boost::flyweights::no_locking
> StringFlyweight;
class CProfileNode
{
NONCOPYABLE(CProfileNode);

View File

@ -487,25 +487,33 @@ namespace
struct DumpTable
{
ScriptInterface& scriptInterface;
CScriptVal root;
ScriptInterface& m_ScriptInterface;
JS::PersistentRooted<JS::Value> m_Root;
DumpTable(ScriptInterface& scriptInterface, JS::HandleValue root) :
scriptInterface(scriptInterface), root(root)
m_ScriptInterface(scriptInterface), m_Root(scriptInterface.GetJSRuntime(), 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.GetJSRuntime(), original.m_Root.get())
{
}
void operator() (AbstractProfileTable* table)
{
JSContext* cx = scriptInterface.GetContext();
JSContext* cx = m_ScriptInterface.GetContext();
JSAutoRequest rq(cx);
JS::RootedValue t(cx);
scriptInterface.Eval(L"({})", &t);
scriptInterface.SetProperty(t, "cols", DumpCols(table));
scriptInterface.SetProperty(t, "data", DumpRows(table));
JS::RootedValue rows(cx, DumpRows(table));
m_ScriptInterface.Eval(L"({})", &t);
m_ScriptInterface.SetProperty(t, "cols", DumpCols(table));
m_ScriptInterface.SetProperty(t, "data", rows);
JS::RootedValue tmpRoot(cx, root.get()); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
scriptInterface.SetProperty(tmpRoot, table->GetTitle().c_str(), t);
m_ScriptInterface.SetProperty(m_Root, table->GetTitle().c_str(), t);
}
std::vector<std::string> DumpCols(AbstractProfileTable* table)
@ -520,30 +528,33 @@ namespace
return titles;
}
CScriptVal DumpRows(AbstractProfileTable* table)
JS::Value DumpRows(AbstractProfileTable* table)
{
JSContext* cx = scriptInterface.GetContext();
JSContext* cx = m_ScriptInterface.GetContext();
JSAutoRequest rq(cx);
JS::RootedValue data(cx);
scriptInterface.Eval("({})", &data);
m_ScriptInterface.Eval("({})", &data);
const std::vector<ProfileColumn>& columns = table->GetColumns();
for (size_t r = 0; r < table->GetNumberRows(); ++r)
{
JS::RootedValue row(cx);
scriptInterface.Eval("([])", &row);
scriptInterface.SetProperty(data, table->GetCellText(r, 0).c_str(), row);
m_ScriptInterface.Eval("([])", &row);
m_ScriptInterface.SetProperty(data, table->GetCellText(r, 0).c_str(), row);
if (table->GetChild(r))
scriptInterface.SetPropertyInt(row, 0, DumpRows(table->GetChild(r)));
{
JS::RootedValue childRows(cx, DumpRows(table->GetChild(r)));
m_ScriptInterface.SetPropertyInt(row, 0, childRows);
}
for (size_t c = 1; c < columns.size(); ++c)
scriptInterface.SetPropertyInt(row, c, table->GetCellText(r, c));
m_ScriptInterface.SetPropertyInt(row, c, table->GetCellText(r, c));
}
return data.get();
return data;
}
private:
@ -592,7 +603,7 @@ void CProfileViewer::SaveToFile()
m->outputStream.flush();
}
CScriptVal CProfileViewer::SaveToJS(ScriptInterface& scriptInterface)
JS::Value CProfileViewer::SaveToJS(ScriptInterface& scriptInterface)
{
JSContext* cx = scriptInterface.GetContext();
JSAutoRequest rq(cx);
@ -604,7 +615,7 @@ CScriptVal CProfileViewer::SaveToJS(ScriptInterface& scriptInterface)
sort(tables.begin(), tables.end(), SortByName);
for_each(tables.begin(), tables.end(), DumpTable(scriptInterface, root));
return root.get();
return root;
}
void CProfileViewer::ShowTable(const CStr& table)

View File

@ -27,7 +27,11 @@
#include "ps/Singleton.h"
class ScriptInterface;
class CScriptVal;
namespace JS
{
class Value;
}
/**
* Struct ProfileColumn: Describes one column of an AbstractProfileTable.
@ -187,7 +191,7 @@ public:
* SaveToJS: Return a script value containing the current profiler data
* (for all profile tables).
*/
CScriptVal SaveToJS(ScriptInterface& scriptInterface);
JS::Value SaveToJS(ScriptInterface& scriptInterface);
/**
* ShowTable: Set the named profile table to be the displayed one. If it

View File

@ -80,7 +80,7 @@ void CReplayLogger::StartGame(JS::MutableHandleValue attribs)
*m_Stream << "start " << m_ScriptInterface.StringifyJSON(attribs, false) << "\n";
}
void CReplayLogger::Turn(u32 n, u32 turnLength, const std::vector<SimulationCommand>& commands)
void CReplayLogger::Turn(u32 n, u32 turnLength, std::vector<SimulationCommand>& commands)
{
JSContext* cx = m_ScriptInterface.GetContext();
JSAutoRequest rq(cx);
@ -88,9 +88,7 @@ void CReplayLogger::Turn(u32 n, u32 turnLength, const std::vector<SimulationComm
*m_Stream << "turn " << n << " " << turnLength << "\n";
for (size_t i = 0; i < commands.size(); ++i)
{
// TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
JS::RootedValue tmpCommand(cx, commands[i].data.get());
*m_Stream << "cmd " << commands[i].player << " " << m_ScriptInterface.StringifyJSON(&tmpCommand, false) << "\n";
*m_Stream << "cmd " << commands[i].player << " " << m_ScriptInterface.StringifyJSON(&commands[i].data, false) << "\n";
}
*m_Stream << "end\n";
m_Stream->flush();
@ -135,7 +133,7 @@ void CReplayPlayer::Replay(bool serializationtest)
const int runtimeSize = 384 * 1024 * 1024;
const int heapGrowthBytesGCTrigger = 20 * 1024 * 1024;
g_ScriptRuntime = ScriptInterface::CreateRuntime(runtimeSize, heapGrowthBytesGCTrigger);
g_ScriptRuntime = ScriptInterface::CreateRuntime(shared_ptr<ScriptRuntime>(), runtimeSize, heapGrowthBytesGCTrigger);
CGame game(true);
g_Game = &game;
@ -169,7 +167,7 @@ void CReplayPlayer::Replay(bool serializationtest)
JS::RootedValue attribs(cx);
ENSURE(game.GetSimulation2()->GetScriptInterface().ParseJSON(line, &attribs));
game.StartGame(CScriptValRooted(cx, attribs), "");
game.StartGame(&attribs, "");
// TODO: Non progressive load can fail - need a decent way to handle this
LDR_NonprogressiveLoad();
@ -192,8 +190,7 @@ void CReplayPlayer::Replay(bool serializationtest)
JS::RootedValue data(cx);
game.GetSimulation2()->GetScriptInterface().ParseJSON(line, &data);
SimulationCommand cmd = { player, CScriptValRooted(cx, data) };
commands.push_back(cmd);
commands.emplace_back(SimulationCommand(player, cx, data));
}
else if (type == "hash" || type == "hash-quick")
{

View File

@ -20,7 +20,6 @@
#include "scriptinterface/ScriptTypes.h"
class CScriptValRooted;
struct SimulationCommand;
class ScriptInterface;
@ -42,7 +41,7 @@ public:
/**
* Run the given turn with the given collection of player commands.
*/
virtual void Turn(u32 n, u32 turnLength, const std::vector<SimulationCommand>& commands) = 0;
virtual void Turn(u32 n, u32 turnLength, std::vector<SimulationCommand>& commands) = 0;
/**
* Optional hash of simulation state (for sync checking).
@ -72,7 +71,7 @@ public:
~CReplayLogger();
virtual void StartGame(JS::MutableHandleValue attribs);
virtual void Turn(u32 n, u32 turnLength, const std::vector<SimulationCommand>& commands);
virtual void Turn(u32 n, u32 turnLength, std::vector<SimulationCommand>& commands);
virtual void Hash(const std::string& hash, bool quick);
private:

View File

@ -57,6 +57,7 @@ Status SavedGames::Save(const std::wstring& name, const std::wstring& descriptio
{
JSContext* cx = simulation.GetScriptInterface().GetContext();
JSAutoRequest rq(cx);
// Determine the filename to save under
const VfsPath basenameFormat(L"saves/" + name);
const VfsPath filename = basenameFormat.ChangeExtension(L".0adsave");
@ -81,13 +82,14 @@ Status SavedGames::Save(const std::wstring& name, const std::wstring& descriptio
WARN_RETURN(ERR::FAIL);
JS::RootedValue metadata(cx);
JS::RootedValue initAttributes(cx, simulation.GetInitAttributes());
simulation.GetScriptInterface().Eval("({})", &metadata);
simulation.GetScriptInterface().SetProperty(metadata, "version_major", SAVED_GAME_VERSION_MAJOR);
simulation.GetScriptInterface().SetProperty(metadata, "version_minor", SAVED_GAME_VERSION_MINOR);
simulation.GetScriptInterface().SetProperty(metadata, "mods", g_modsLoaded);
simulation.GetScriptInterface().SetProperty(metadata, "time", (double)now);
simulation.GetScriptInterface().SetProperty(metadata, "player", playerID);
simulation.GetScriptInterface().SetProperty(metadata, "initAttributes", simulation.GetInitAttributes());
simulation.GetScriptInterface().SetProperty(metadata, "initAttributes", initAttributes);
JS::RootedValue guiMetadata(cx);
simulation.GetScriptInterface().ReadStructuredClone(guiMetadataClone, &guiMetadata);
@ -150,7 +152,9 @@ public:
* types and confusing (a chain of pointers pointing to other pointers).
*/
CGameLoader(ScriptInterface& scriptInterface, std::string* savedState) :
m_ScriptInterface(scriptInterface), m_SavedState(savedState)
m_ScriptInterface(scriptInterface),
m_Metadata(scriptInterface.GetJSRuntime()),
m_SavedState(savedState)
{
}
@ -169,9 +173,7 @@ public:
std::string buffer;
buffer.resize(fileInfo.Size());
WARN_IF_ERR(archiveFile->Load("", DummySharedPtr((u8*)buffer.data()), buffer.size()));
JS::RootedValue tmpMetadata(cx); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
m_ScriptInterface.ParseJSON(buffer, &tmpMetadata);
m_Metadata = CScriptValRooted(cx, tmpMetadata);
m_ScriptInterface.ParseJSON(buffer, &m_Metadata);
}
else if (pathname == L"simulation.dat" && m_SavedState)
{
@ -188,7 +190,7 @@ public:
private:
ScriptInterface& m_ScriptInterface;
CScriptValRooted m_Metadata;
JS::PersistentRooted<JS::Value> m_Metadata;
std::string* m_SavedState;
};
@ -216,13 +218,13 @@ Status SavedGames::Load(const std::wstring& name, ScriptInterface& scriptInterfa
return INFO::OK;
}
std::vector<CScriptValRooted> SavedGames::GetSavedGames(ScriptInterface& scriptInterface)
JS::Value SavedGames::GetSavedGames(ScriptInterface& scriptInterface)
{
TIMER(L"GetSavedGames");
JSContext* cx = scriptInterface.GetContext();
JSAutoRequest rq(cx);
std::vector<CScriptValRooted> games;
JS::RootedObject games(cx, JS_NewArrayObject(cx, 0));
Status err;
@ -261,10 +263,10 @@ std::vector<CScriptValRooted> SavedGames::GetSavedGames(ScriptInterface& scriptI
scriptInterface.Eval("({})", &game);
scriptInterface.SetProperty(game, "id", pathnames[i].Basename());
scriptInterface.SetProperty(game, "metadata", metadata);
games.push_back(CScriptValRooted(cx, game));
JS_SetElement(cx, games, i, game);
}
return games;
return JS::ObjectValue(*games);
}
bool SavedGames::DeleteSavedGame(const std::wstring& name)
@ -289,7 +291,7 @@ bool SavedGames::DeleteSavedGame(const std::wstring& name)
return true;
}
CScriptValRooted SavedGames::GetEngineInfo(ScriptInterface& scriptInterface)
JS::Value SavedGames::GetEngineInfo(ScriptInterface& scriptInterface)
{
JSContext* cx = scriptInterface.GetContext();
JSAutoRequest rq(cx);
@ -299,6 +301,6 @@ CScriptValRooted SavedGames::GetEngineInfo(ScriptInterface& scriptInterface)
scriptInterface.SetProperty(metainfo, "version_major", SAVED_GAME_VERSION_MAJOR);
scriptInterface.SetProperty(metainfo, "version_minor", SAVED_GAME_VERSION_MINOR);
scriptInterface.SetProperty(metainfo, "mods" , g_modsLoaded);
return CScriptValRooted(cx, metainfo);
return metainfo;
}

View File

@ -20,8 +20,6 @@
#include "scriptinterface/ScriptInterface.h"
class CSimulation2;
class ScriptInterface;
class CScriptValRooted;
class CGUIManager;
/**
@ -80,9 +78,9 @@ Status Load(const std::wstring& name, ScriptInterface& scriptInterface, JS::Muta
* Get list of saved games for GUI script usage
*
* @param scriptInterface the ScriptInterface in which to create the return data.
* @return list of objects containing saved game data
* @return array of objects containing saved game data
*/
std::vector<CScriptValRooted> GetSavedGames(ScriptInterface& scriptInterface);
JS::Value GetSavedGames(ScriptInterface& scriptInterface);
/**
* Permanently deletes the saved game archive with the given name
@ -98,7 +96,7 @@ bool DeleteSavedGame(const std::wstring& name);
* @param scriptInterface the ScriptInterface in which to create the return data.
* @return list of objects containing saved game data
*/
CScriptValRooted GetEngineInfo(ScriptInterface& scriptInterface);
JS::Value GetEngineInfo(ScriptInterface& scriptInterface);
}

View File

@ -227,15 +227,34 @@ std::vector<std::string> CTemplateLoader::FindPlaceableTemplates(const std::stri
{
JS::RootedValue placeablesFilter(cx);
scriptInterface.ReadJSONFile("simulation/data/placeablesFilter.json", &placeablesFilter);
std::vector<CScriptValRooted> folders;
if (scriptInterface.GetProperty(placeablesFilter, "templates", folders))
JS::RootedObject folders(cx);
if (scriptInterface.GetProperty(placeablesFilter, "templates", &folders))
{
if (!(JS_IsArrayObject(cx, folders)))
{
LOGERROR("FindPlaceableTemplates: Argument must be an array!");
return templates;
}
u32 length;
if (!JS_GetArrayLength(cx, folders, &length))
{
LOGERROR("FindPlaceableTemplates: Failed to get array length!");
return templates;
}
templatePath = VfsPath(TEMPLATE_ROOT) / path;
//I have every object inside, just run for each
for (std::vector<CScriptValRooted>::iterator iterator = folders.begin(); iterator != folders.end();++iterator)
for (u32 i=0; i<length; ++i)
{
JS::RootedValue val(cx, (*iterator).get());
JS::RootedValue val(cx);
if (!JS_GetElement(cx, folders, i, &val))
{
LOGERROR("FindPlaceableTemplates: Failed to read array element!");
return templates;
}
std::string directoryPath;
std::wstring fileFilter;
scriptInterface.GetProperty(val, "directory", directoryPath);

View File

@ -65,7 +65,7 @@ CWorld::CWorld(CGame *pGame):
/**
* Initializes the game world with the attributes provided.
**/
void CWorld::RegisterInit(const CStrW& mapFile, const CScriptValRooted& settings, int playerID)
void CWorld::RegisterInit(const CStrW& mapFile, JSRuntime* rt, JS::HandleValue settings, int playerID)
{
// Load the map, if one was specified
if (mapFile.length())
@ -77,7 +77,7 @@ void CWorld::RegisterInit(const CStrW& mapFile, const CScriptValRooted& settings
{
reader = new CMapReader;
CTriggerManager* pTriggerManager = NULL;
reader->LoadMap(mapfilename, settings, m_Terrain,
reader->LoadMap(mapfilename, rt, settings, m_Terrain,
CRenderer::IsInitialised() ? g_Renderer.GetWaterManager() : NULL,
CRenderer::IsInitialised() ? g_Renderer.GetSkyManager() : NULL,
&g_LightEnv, m_pGame->GetView(),
@ -95,14 +95,14 @@ void CWorld::RegisterInit(const CStrW& mapFile, const CScriptValRooted& settings
}
}
void CWorld::RegisterInitRMS(const CStrW& scriptFile, const CScriptValRooted& settings, int playerID)
void CWorld::RegisterInitRMS(const CStrW& scriptFile, JSRuntime* rt, JS::HandleValue settings, int playerID)
{
// If scriptFile is empty, a blank map will be generated using settings (no RMS run)
CMapReader* reader = 0;
reader = new CMapReader;
CTriggerManager* pTriggerManager = NULL;
reader->LoadRandomMap(scriptFile, settings, m_Terrain,
reader->LoadRandomMap(scriptFile, rt, settings, m_Terrain,
CRenderer::IsInitialised() ? g_Renderer.GetWaterManager() : NULL,
CRenderer::IsInitialised() ? g_Renderer.GetSkyManager() : NULL,
&g_LightEnv, m_pGame->GetView(),

View File

@ -73,12 +73,12 @@ public:
/*
Initialize the World - load the map and all objects
*/
void RegisterInit(const CStrW& mapFile, const CScriptValRooted& settings, int playerID);
void RegisterInit(const CStrW& mapFile, JSRuntime* rt, JS::HandleValue settings, int playerID);
/*
Initialize the World - generate and load the random map
*/
void RegisterInitRMS(const CStrW& scriptFile, const CScriptValRooted& settings, int playerID);
void RegisterInitRMS(const CStrW& scriptFile, JSRuntime* rt, JS::HandleValue settings, int playerID);
/**
* Get the pointer to the terrain object.

View File

@ -43,12 +43,12 @@ extern void restart_engine();
* @return JS object with available mods as the keys of the modname.json
* properties.
*/
CScriptVal JSI_Mod::GetAvailableMods(ScriptInterface::CxPrivate* pCxPrivate)
JS::Value JSI_Mod::GetAvailableMods(ScriptInterface::CxPrivate* pCxPrivate)
{
ScriptInterface* scriptInterface = pCxPrivate->pScriptInterface;
JSContext* cx = scriptInterface->GetContext();
JSAutoRequest rq(cx);
JS::RootedObject obj(cx, JS_NewObject(cx, NULL, NULL, NULL));
JS::RootedObject obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
const Paths paths(g_args);
@ -80,7 +80,7 @@ CScriptVal JSI_Mod::GetAvailableMods(ScriptInterface::CxPrivate* pCxPrivate)
continue;
// Valid mod, add it to our structure
JS_SetProperty(cx, obj, utf8_from_wstring(iter->string()).c_str(), json.address());
JS_SetProperty(cx, obj, utf8_from_wstring(iter->string()).c_str(), json);
}
GetDirectoryEntries(modUserPath, NULL, &modDirsUser);
@ -106,7 +106,7 @@ CScriptVal JSI_Mod::GetAvailableMods(ScriptInterface::CxPrivate* pCxPrivate)
continue;
// Valid mod, add it to our structure
JS_SetProperty(cx, obj, utf8_from_wstring(iter->string()).c_str(), json.address());
JS_SetProperty(cx, obj, utf8_from_wstring(iter->string()).c_str(), json);
}
return JS::ObjectValue(*obj);
@ -124,7 +124,7 @@ void JSI_Mod::SetMods(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), std::vecto
void JSI_Mod::RegisterScriptFunctions(ScriptInterface& scriptInterface)
{
scriptInterface.RegisterFunction<CScriptVal, &JSI_Mod::GetAvailableMods>("GetAvailableMods");
scriptInterface.RegisterFunction<JS::Value, &JSI_Mod::GetAvailableMods>("GetAvailableMods");
scriptInterface.RegisterFunction<void, &JSI_Mod::RestartEngine>("RestartEngine");
scriptInterface.RegisterFunction<void, std::vector<CStr>, &JSI_Mod::SetMods>("SetMods");
}

View File

@ -19,12 +19,11 @@
#define INCLUDED_JSI_MOD
class ScriptInterface;
class CScriptVal;
namespace JSI_Mod
{
void RegisterScriptFunctions(ScriptInterface& scriptInterface);
CScriptVal GetAvailableMods(ScriptInterface::CxPrivate* pCxPrivate);
JS::Value GetAvailableMods(ScriptInterface::CxPrivate* pCxPrivate);
void RestartEngine(ScriptInterface::CxPrivate* pCxPrivate);
void SetMods(ScriptInterface::CxPrivate* pCxPrivate, std::vector<CStr> mods);
}

View File

@ -46,20 +46,14 @@
struct BuildDirEntListState
{
JSContext* cx;
JSObject* filename_array;
JS::PersistentRootedObject filename_array;
int cur_idx;
BuildDirEntListState(JSContext* cx_)
: cx(cx_)
: cx(cx_),
filename_array(cx, JS_NewArrayObject(cx, JS::HandleValueArray::empty())),
cur_idx(0)
{
filename_array = JS_NewArrayObject(cx, 0, NULL);
JS_AddObjectRoot(cx, &filename_array);
cur_idx = 0;
}
~BuildDirEntListState()
{
JS_RemoveObjectRoot(cx, &filename_array);
}
};
@ -67,10 +61,12 @@ struct BuildDirEntListState
static Status BuildDirEntListCB(const VfsPath& pathname, const CFileInfo& UNUSED(fileINfo), uintptr_t cbData)
{
BuildDirEntListState* s = (BuildDirEntListState*)cbData;
JSAutoRequest rq(s->cx);
JS::RootedObject filenameArrayObj(s->cx, s->filename_array);
JS::RootedValue val(s->cx);
ScriptInterface::ToJSVal( s->cx, &val, CStrW(pathname.string()) );
JS_SetElement(s->cx, s->filename_array, s->cur_idx++, val.address());
JS_SetElement(s->cx, filenameArrayObj, s->cur_idx++, val);
return INFO::OK;
}
@ -85,7 +81,7 @@ static Status BuildDirEntListCB(const VfsPath& pathname, const CFileInfo& UNUSED
//
// note: full pathnames of each file/subdirectory are returned,
// ready for use as a "filename" for the other functions.
CScriptVal JSI_VFS::BuildDirEntList(ScriptInterface::CxPrivate* pCxPrivate, std::wstring path, std::wstring filterStr, bool recurse)
JS::Value JSI_VFS::BuildDirEntList(ScriptInterface::CxPrivate* pCxPrivate, std::wstring path, std::wstring filterStr, bool recurse)
{
// convert to const wchar_t*; if there's no filter, pass 0 for speed
// (interpreted as: "accept all files without comparing").
@ -144,7 +140,7 @@ unsigned int JSI_VFS::GetFileSize(ScriptInterface::CxPrivate* UNUSED(pCxPrivate)
//
// contents = readFile(filename);
// filename: VFS filename (may include path)
CScriptVal JSI_VFS::ReadFile(ScriptInterface::CxPrivate* pCxPrivate, std::wstring filename)
JS::Value JSI_VFS::ReadFile(ScriptInterface::CxPrivate* pCxPrivate, std::wstring filename)
{
JSContext* cx = pCxPrivate->pScriptInterface->GetContext();
JSAutoRequest rq(cx);
@ -161,7 +157,7 @@ CScriptVal JSI_VFS::ReadFile(ScriptInterface::CxPrivate* pCxPrivate, std::wstrin
// Decode as UTF-8
JS::RootedValue ret(cx);
ScriptInterface::ToJSVal(cx, &ret, contents.FromUTF8());
return ret.get();
return ret;
}
@ -169,7 +165,7 @@ CScriptVal JSI_VFS::ReadFile(ScriptInterface::CxPrivate* pCxPrivate, std::wstrin
//
// lines = readFileLines(filename);
// filename: VFS filename (may include path)
CScriptVal JSI_VFS::ReadFileLines(ScriptInterface::CxPrivate* pCxPrivate, std::wstring filename)
JS::Value JSI_VFS::ReadFileLines(ScriptInterface::CxPrivate* pCxPrivate, std::wstring filename)
{
JSContext* cx = pCxPrivate->pScriptInterface->GetContext();
JSAutoRequest rq(cx);
@ -190,16 +186,16 @@ CScriptVal JSI_VFS::ReadFileLines(ScriptInterface::CxPrivate* pCxPrivate, std::w
//
std::stringstream ss(contents);
JSObject* line_array = JS_NewArrayObject(cx, 0, NULL);
JS::RootedObject line_array(cx, JS_NewArrayObject(cx, JS::HandleValueArray::empty()));
std::string line;
int cur_line = 0;
while (std::getline(ss, line))
{
// Decode each line as UTF-8
JS::RootedValue val(cx);
ScriptInterface::ToJSVal(cx, &val, CStr(line).FromUTF8());
JS_SetElement(cx, line_array, cur_line++, val.address());
JS_SetElement(cx, line_array, cur_line++, val);
}
return JS::ObjectValue(*line_array);

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2009 Wildfire Games.
/* Copyright (C) 2014 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -38,7 +38,7 @@ namespace JSI_VFS
//
// note: full pathnames of each file/subdirectory are returned,
// ready for use as a "filename" for the other functions.
CScriptVal BuildDirEntList(ScriptInterface::CxPrivate* pCxPrivate, std::wstring path, std::wstring filterStr, bool recurse);
JS::Value BuildDirEntList(ScriptInterface::CxPrivate* pCxPrivate, std::wstring path, std::wstring filterStr, bool recurse);
// Return true iff the file exists
//
@ -62,13 +62,13 @@ namespace JSI_VFS
//
// contents = readFile(filename);
// filename: VFS filename (may include path)
CScriptVal ReadFile(ScriptInterface::CxPrivate* pCxPrivate, std::wstring filename);
JS::Value ReadFile(ScriptInterface::CxPrivate* pCxPrivate, std::wstring filename);
// Return file contents as an array of lines.
//
// lines = readFileLines(filename);
// filename: VFS filename (may include path)
CScriptVal ReadFileLines(ScriptInterface::CxPrivate* pCxPrivate, std::wstring filename);
JS::Value ReadFileLines(ScriptInterface::CxPrivate* pCxPrivate, std::wstring filename);
}
#endif

View File

@ -1,61 +0,0 @@
/* Copyright (C) 2010 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 "AutoRooters.h"
#include "scriptinterface/ScriptInterface.h"
AutoGCRooter::AutoGCRooter(ScriptInterface& scriptInterface)
: m_ScriptInterface(scriptInterface)
{
m_Previous = m_ScriptInterface.ReplaceAutoGCRooter(this);
}
AutoGCRooter::~AutoGCRooter()
{
AutoGCRooter* r = m_ScriptInterface.ReplaceAutoGCRooter(m_Previous);
ENSURE(r == this); // must be correctly nested
}
void AutoGCRooter::Trace(JSTracer* trc)
{
if (m_Previous)
m_Previous->Trace(trc);
for (size_t i = 0; i < m_Objects.size(); ++i)
{
JS_CallObjectTracer(trc, &m_Objects[i], "AutoGCRooter object");
}
for (size_t i = 0; i < m_Vals.size(); ++i)
{
JS_CallValueTracer(trc, &m_Vals[i], "AutoGCRooter val");
}
for (size_t i = 0; i < m_IdArrays.size(); ++i)
{
for (int j = 0; j < JS_IdArrayLength(m_ScriptInterface.GetContext(), m_IdArrays[i]); ++j)
{
jsval val = JSVAL_VOID;
JS_IdToValue(m_ScriptInterface.GetContext(), JS_IdArrayGet(m_ScriptInterface.GetContext(), m_IdArrays[i], j), &val);
JS_CallValueTracer(trc, &val, "AutoGCRooter id array");
}
}
}

View File

@ -1,52 +0,0 @@
/* Copyright (C) 2010 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_AUTOROOTERS
#define INCLUDED_AUTOROOTERS
#include "scriptinterface/ScriptTypes.h"
/**
* Helper for rooting large groups of script values.
* Construct this object, push values into it, and they will all be rooted until this
* object is destroyed.
* Many of these objects can be used at once, but their lifetimes must be correctly nested.
*/
class AutoGCRooter
{
NONCOPYABLE(AutoGCRooter);
public:
AutoGCRooter(ScriptInterface& scriptInterface);
~AutoGCRooter();
void Push(JSObject* obj) { m_Objects.push_back(obj); }
void Push(jsval val) { m_Vals.push_back(val); }
void Push(JSIdArray* ida) { m_IdArrays.push_back(ida); }
void Trace(JSTracer* trc);
private:
ScriptInterface& m_ScriptInterface;
AutoGCRooter* m_Previous;
std::vector<JSObject*> m_Objects;
std::vector<jsval> m_Vals;
std::vector<JSIdArray*> m_IdArrays;
// TODO: add vectors of other value types
};
#endif // INCLUDED_AUTOROOTERS

View File

@ -18,6 +18,39 @@
#include <boost/preprocessor/punctuation/comma_if.hpp>
#include <boost/preprocessor/repetition/repeat.hpp>
private:
/**
* In our interface code (the CONVERT_ARG macro specifically) we require types to be default-constructible.
* This is a workaround to make the current design work with JS::HandleValue types, which have a private constructor.
* JS::HandleValue objects are meant to be implicitly created only from JS::RootedValue objects.
* Generally handles should not be used this way, but in this case we can be sure that the handle will not live longer than its root,
* so it should be OK.
* This solution involves some overhead, but it should be quite small and shouldn't affect performance in practice.
* HandleValue types are just structs with one pointer and fit into a single register.
*/
class HandleWrapper
{
public:
HandleWrapper() : m_Handle(JS::NullHandleValue) {};
void set(JS::HandleValue handle) { m_Handle.repoint(handle); }
operator JS::HandleValue()
{
return m_Handle;
}
private:
JS::HandleValue m_Handle;
};
// WrapperIfHandle<T>::Type has the type HandleWrapper for T == JS::HandleValue and
// T for all other types.
// Allows to use default-constructible HandleWrapper types in templates instead of the
// HandleValue type that isn't default-constructible without code duplication.
template <typename T> struct WrapperIfHandle;
public:
// Define lots of useful macros:
// Varieties of comma-separated list to fit on the head/tail/whole of another comma-separated list
@ -27,7 +60,19 @@
// Some other things
#define TYPED_ARGS(z, i, data) , T##i a##i
#define TYPED_ARGS_CONST_REF(z, i, data) const T##i& a##i,
#define CONVERT_ARG(z, i, data) T##i a##i; if (! ScriptInterface::FromJSVal<T##i>(cx, i < args.length() ? args.handleAt(i) : JS::UndefinedHandleValue, a##i)) return false;
// TODO: We allow optional parameters when the C++ type can be converted from JS::UndefinedValue.
// FromJSVal is expected to either set a##i or return false (otherwise we could get undefined
// behaviour because some types have undefined values when not being initialized).
// This is not very clear and also a bit fragile. Another problem is that the error reporting lacks
// a bit. SpiderMonkey will throw a JS exception and abort the execution of the current function when
// we return false here (without printing a callstack or additional detail telling that an argument
// conversion failed). So we have two TODOs here:
// 1. On the conceptual side: How to consistently work with optional parameters (or drop them completely?)
// 2. On the technical side: Improve error handling, find a better way to ensure parameters are initialized
#define CONVERT_ARG(z, i, data) \
typename WrapperIfHandle<T##i>::Type a##i; \
if (! ScriptInterface::FromJSVal<typename WrapperIfHandle<T##i>::Type>(cx, i < args.length() ? args[i] : JS::UndefinedHandleValue, a##i)) return false;
// List-generating macros, named roughly after their first list item
#define TYPENAME_T0_HEAD(z, i) BOOST_PP_REPEAT_##z (i, NUMBERED_LIST_HEAD, typename T) // "typename T0, typename T1, "
@ -35,8 +80,8 @@
#define T0(z, i) BOOST_PP_REPEAT_##z (i, NUMBERED_LIST_BALANCED, T) // "T0, T1"
#define T0_HEAD(z, i) BOOST_PP_REPEAT_##z (i, NUMBERED_LIST_HEAD, T) // "T0, T1, "
#define T0_TAIL(z, i) BOOST_PP_REPEAT_##z (i, NUMBERED_LIST_TAIL, T) // ", T0, T1"
#define T0_A0(z, i) BOOST_PP_REPEAT_##z (i, TYPED_ARGS, ~) // "T0 a0, T1 a1"
#define T0_A0_CONST_REF(z, i) BOOST_PP_REPEAT_##z (i, TYPED_ARGS_CONST_REF, ~) // " const T0 a0, const T1 a1, "
#define T0_A0(z, i) BOOST_PP_REPEAT_##z (i, TYPED_ARGS, ~) // ",T0 a0, T1 a1"
#define T0_A0_CONST_REF(z, i) BOOST_PP_REPEAT_##z (i, TYPED_ARGS_CONST_REF, ~) // ", const T0 a0, const T1 a1, "
#define A0(z, i) BOOST_PP_REPEAT_##z (i, NUMBERED_LIST_BALANCED, a) // "a0, a1"
#define A0_TAIL(z, i) BOOST_PP_REPEAT_##z (i, NUMBERED_LIST_TAIL, a) // ", a0, a1"
@ -53,14 +98,14 @@ BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
// (Definition comes later, since it depends on some things we haven't defined yet)
#define OVERLOADS(z, i, data) \
template <typename R, TYPENAME_T0_HEAD(z,i) R (*fptr) ( ScriptInterface::CxPrivate* T0_TAIL(z,i) )> \
static JSBool call(JSContext* cx, uint argc, jsval* vp);
static bool call(JSContext* cx, uint argc, jsval* vp);
BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
#undef OVERLOADS
// Similar, for class methods
#define OVERLOADS(z, i, data) \
template <typename R, TYPENAME_T0_HEAD(z,i) JSClass* CLS, typename TC, R (TC::*fptr) ( T0(z,i) )> \
static JSBool callMethod(JSContext* cx, uint argc, jsval* vp);
static bool callMethod(JSContext* cx, uint argc, jsval* vp);
BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
#undef OVERLOADS

View File

@ -14,7 +14,17 @@
* 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 "ps/GameSetup/Config.h"
#include "ps/GameSetup/Config.h"
template <typename T> struct ScriptInterface::WrapperIfHandle
{
typedef T Type;
};
template <> struct ScriptInterface::WrapperIfHandle<JS::HandleValue>
{
typedef HandleWrapper Type;
};
// (NativeWrapperDecls.h set up a lot of the macros we use here)
@ -27,8 +37,7 @@ struct ScriptInterface_NativeWrapper {
#define OVERLOADS(z, i, data) \
template<TYPENAME_T0_HEAD(z,i) typename F> \
static void call(JSContext* cx, JS::MutableHandleValue rval, F fptr T0_A0(z,i)) { \
ScriptInterface* pScriptInterface = ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface; \
pScriptInterface->AssignOrToJSVal<R>(rval, fptr(ScriptInterface::GetScriptInterfaceAndCBData(cx) A0_TAIL(z,i))); \
ScriptInterface::AssignOrToJSValUnrooted<R>(cx, rval, fptr(ScriptInterface::GetScriptInterfaceAndCBData(cx) A0_TAIL(z,i))); \
}
BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
@ -54,8 +63,7 @@ struct ScriptInterface_NativeMethodWrapper {
#define OVERLOADS(z, i, data) \
template<TYPENAME_T0_HEAD(z,i) typename F> \
static void call(JSContext* cx, JS::MutableHandleValue rval, TC* c, F fptr T0_A0(z,i)) { \
ScriptInterface* pScriptInterface = ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface; \
pScriptInterface->AssignOrToJSVal<R>(rval, (c->*fptr)( A0(z,i) )); \
ScriptInterface::AssignOrToJSValUnrooted<R>(cx, rval, (c->*fptr)( A0(z,i) )); \
}
BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
@ -76,30 +84,29 @@ struct ScriptInterface_NativeMethodWrapper<void, TC> {
// Fast natives don't trigger the hook we use for profiling, so explicitly
// notify the profiler when these functions are being called.
// ScriptInterface_impl::Register stores the name in a reserved slot.
// (TODO: this doesn't work for functions registered via InterfaceScripted.h.
// Maybe we should do some interned JS_GetFunctionId thing.)
#define SCRIPT_PROFILE \
if (g_ScriptProfilingEnabled) \
{ \
ENSURE(JS_CALLEE(cx, vp).isObject() && JS_ObjectIsFunction(cx, &JS_CALLEE(cx, vp).toObject())); \
const char* name = "(unknown)"; \
jsval nameval; \
nameval = JS_GetReservedSlot( &JS_CALLEE(cx, vp).toObject(), 0); \
if (!nameval.isUndefined()) \
name = static_cast<const char*>(JSVAL_TO_PRIVATE(nameval)); \
CProfileSampleScript profile(name); \
std::string name = "(unknown)"; \
JS::RootedString str(cx, JS_GetFunctionId(JS_ValueToFunction(cx, args.calleev()))); \
if (str) \
{ \
JS::RootedValue strVal(cx, JS::StringValue(str)); \
ScriptInterface::FromJSVal(cx, strVal, name); \
} \
CProfileSampleScript profile(StringFlyweight(name).get().c_str()); \
}
// JSFastNative-compatible function that wraps the function identified in the template argument list
#define OVERLOADS(z, i, data) \
template <typename R, TYPENAME_T0_HEAD(z,i) R (*fptr) ( ScriptInterface::CxPrivate* T0_TAIL(z,i) )> \
JSBool ScriptInterface::call(JSContext* cx, uint argc, jsval* vp) { \
bool ScriptInterface::call(JSContext* cx, uint argc, jsval* vp) { \
JS::CallArgs args = JS::CallArgsFromVp(argc, vp); \
JSAutoRequest rq(cx); \
SCRIPT_PROFILE \
BOOST_PP_REPEAT_##z (i, CONVERT_ARG, ~) \
JS::RootedValue rval(cx); \
ScriptInterface_NativeWrapper<R>::call(cx, &rval, fptr A0_TAIL(z,i)); \
ScriptInterface_NativeWrapper<R>::template call<T0_HEAD(z,i) R( ScriptInterface::CxPrivate* T0_TAIL(z,i))>(cx, &rval, fptr A0_TAIL(z,i)); \
args.rval().set(rval); \
return !ScriptInterface::IsExceptionPending(cx); \
}
@ -109,7 +116,7 @@ BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
// Same idea but for methods
#define OVERLOADS(z, i, data) \
template <typename R, TYPENAME_T0_HEAD(z,i) JSClass* CLS, typename TC, R (TC::*fptr) ( T0(z,i) )> \
JSBool ScriptInterface::callMethod(JSContext* cx, uint argc, jsval* vp) { \
bool ScriptInterface::callMethod(JSContext* cx, uint argc, jsval* vp) { \
JS::CallArgs args = JS::CallArgsFromVp(argc, vp); \
JSAutoRequest rq(cx); \
SCRIPT_PROFILE \
@ -126,7 +133,7 @@ BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
#undef OVERLOADS
#define ASSIGN_OR_TO_JS_VAL(z, i, data) AssignOrToJSVal(argv.handleAt(i), a##i);
#define ASSIGN_OR_TO_JS_VAL(z, i, data) AssignOrToJSVal(cx, argv.handleAt(i), a##i);
#define OVERLOADS(z, i, data) \
template<typename R TYPENAME_T0_TAIL(z, i)> \
@ -138,7 +145,7 @@ bool ScriptInterface::CallFunction(JS::HandleValue val, const char* name, T0_A0_
JS::AutoValueVector argv(cx); \
argv.resize(i); \
BOOST_PP_REPEAT_##z (i, ASSIGN_OR_TO_JS_VAL, ~) \
bool ok = CallFunction_(val, name, argv.length(), argv.begin(), &jsRet); \
bool ok = CallFunction_(val, name, argv, &jsRet); \
if (!ok) \
return false; \
return FromJSVal(cx, jsRet, ret); \
@ -156,7 +163,7 @@ bool ScriptInterface::CallFunction(JS::HandleValue val, const char* name, T0_A0_
JS::AutoValueVector argv(cx); \
argv.resize(i); \
BOOST_PP_REPEAT_##z (i, ASSIGN_OR_TO_JS_VAL, ~) \
bool ok = CallFunction_(val, name, argv.length(), argv.begin(), jsRet); \
bool ok = CallFunction_(val, name, argv, jsRet); \
if (!ok) \
return false; \
return true; \
@ -173,7 +180,7 @@ bool ScriptInterface::CallFunction(JS::HandleValue val, const char* name, T0_A0_
JS::AutoValueVector argv(cx); \
argv.resize(i); \
BOOST_PP_REPEAT_##z (i, ASSIGN_OR_TO_JS_VAL, ~) \
bool ok = CallFunction_(val, name, argv.length(), argv.begin(), ret); \
bool ok = CallFunction_(val, name, argv, ret); \
if (!ok) \
return false; \
return true; \

View File

@ -98,6 +98,7 @@ template<> bool ScriptInterface::FromJSVal<u8>(JSContext* cx, JS::HandleValue v,
template<> bool ScriptInterface::FromJSVal<long>(JSContext* cx, JS::HandleValue v, long& out)
{
JSAutoRequest rq(cx);
i64 tmp;
bool ok = JS::ToInt64(cx, v, &tmp);
out = (long)tmp;
@ -106,6 +107,7 @@ template<> bool ScriptInterface::FromJSVal<long>(JSContext* cx, JS::HandleValue
template<> bool ScriptInterface::FromJSVal<unsigned long>(JSContext* cx, JS::HandleValue v, unsigned long& out)
{
JSAutoRequest rq(cx);
u64 tmp;
bool ok = JS::ToUint64(cx, v, &tmp);
out = (unsigned long)tmp;
@ -117,6 +119,7 @@ template<> bool ScriptInterface::FromJSVal<unsigned long>(JSContext* cx, JS::Han
template<> bool ScriptInterface::FromJSVal<size_t>(JSContext* cx, JS::HandleValue v, size_t& out)
{
JSAutoRequest rq(cx);
int tmp;
if(!FromJSVal<int>(cx, v, tmp))
return false;
@ -128,6 +131,7 @@ template<> bool ScriptInterface::FromJSVal<size_t>(JSContext* cx, JS::HandleValu
template<> bool ScriptInterface::FromJSVal<ssize_t>(JSContext* cx, JS::HandleValue v, ssize_t& out)
{
JSAutoRequest rq(cx);
int tmp;
if(!FromJSVal<int>(cx, v, tmp))
return false;
@ -139,23 +143,11 @@ template<> bool ScriptInterface::FromJSVal<ssize_t>(JSContext* cx, JS::HandleVal
#endif
template<> bool ScriptInterface::FromJSVal<CScriptVal>(JSContext* UNUSED(cx), JS::HandleValue v, CScriptVal& out)
{
out = v.get();
return true;
}
template<> bool ScriptInterface::FromJSVal<CScriptValRooted>(JSContext* cx, JS::HandleValue v, CScriptValRooted& out)
{
out = CScriptValRooted(cx, v);
return true;
}
template<> bool ScriptInterface::FromJSVal<std::wstring>(JSContext* cx, JS::HandleValue v, std::wstring& out)
{
JSAutoRequest rq(cx);
WARN_IF_NOT(JSVAL_IS_STRING(v) || JSVAL_IS_NUMBER(v), v); // allow implicit number conversions
JSString* str = JS_ValueToString(cx, v);
WARN_IF_NOT(v.isString() || v.isNumber(), v); // allow implicit number conversions
JS::RootedString str(cx, JS::ToString(cx, v));
if (!str)
FAIL("Argument must be convertible to a string");
size_t length;
@ -179,7 +171,7 @@ template<> bool ScriptInterface::FromJSVal<std::string>(JSContext* cx, JS::Handl
{
JSAutoRequest rq(cx);
WARN_IF_NOT(v.isString() || v.isNumber(), v); // allow implicit number conversions
JSString* str = JS_ValueToString(cx, v);
JS::RootedString str(cx, JS::ToString(cx, v));
if (!str)
FAIL("Argument must be convertible to a string");
char* ch = JS_EncodeString(cx, str); // chops off high byte of each jschar
@ -214,15 +206,15 @@ template<> bool ScriptInterface::FromJSVal<Entity>(JSContext* cx, JS::HandleValu
JS::RootedValue rotation(cx);
// TODO: Report type errors
if(!JS_GetProperty(cx, obj, "player", player.address()) || !FromJSVal(cx, player, out.playerID))
if (!JS_GetProperty(cx, obj, "player", &player) || !FromJSVal(cx, player, out.playerID))
FAIL("Failed to read Entity.player property");
if (!JS_GetProperty(cx, obj, "templateName", templateName.address()) || !FromJSVal(cx, templateName, out.templateName))
if (!JS_GetProperty(cx, obj, "templateName", &templateName) || !FromJSVal(cx, templateName, out.templateName))
FAIL("Failed to read Entity.templateName property");
if (!JS_GetProperty(cx, obj, "id", id.address()) || !FromJSVal(cx, id, out.entityID))
if (!JS_GetProperty(cx, obj, "id", &id) || !FromJSVal(cx, id, out.entityID))
FAIL("Failed to read Entity.id property");
if (!JS_GetProperty(cx, obj, "position", position.address()) || !FromJSVal(cx, position, out.position))
if (!JS_GetProperty(cx, obj, "position", &position) || !FromJSVal(cx, position, out.position))
FAIL("Failed to read Entity.position property");
if (!JS_GetProperty(cx, obj, "rotation", rotation.address()) || !FromJSVal(cx, rotation, out.rotation))
if (!JS_GetProperty(cx, obj, "rotation", &rotation) || !FromJSVal(cx, rotation, out.rotation))
FAIL("Failed to read Entity.rotation property");
return true;
@ -295,21 +287,11 @@ template<> void ScriptInterface::ToJSVal<ssize_t>(JSContext* UNUSED(cx), JS::Mut
#endif
template<> void ScriptInterface::ToJSVal<CScriptVal>(JSContext* UNUSED(cx), JS::MutableHandleValue ret, const CScriptVal& val)
{
ret.set(val.get());
}
template<> void ScriptInterface::ToJSVal<CScriptValRooted>(JSContext* UNUSED(cx), JS::MutableHandleValue ret, const CScriptValRooted& val)
{
ret.set(val.get());
}
template<> void ScriptInterface::ToJSVal<std::wstring>(JSContext* cx, JS::MutableHandleValue ret, const std::wstring& val)
{
JSAutoRequest rq(cx);
utf16string utf16(val.begin(), val.end());
JSString* str = JS_NewUCStringCopyN(cx, reinterpret_cast<const jschar*> (utf16.c_str()), utf16.length());
JS::RootedString str(cx, JS_NewUCStringCopyN(cx, reinterpret_cast<const jschar*> (utf16.c_str()), utf16.length()));
if (str)
ret.setString(str);
else
@ -324,7 +306,7 @@ template<> void ScriptInterface::ToJSVal<Path>(JSContext* cx, JS::MutableHandleV
template<> void ScriptInterface::ToJSVal<std::string>(JSContext* cx, JS::MutableHandleValue ret, const std::string& val)
{
JSAutoRequest rq(cx);
JSString* str = JS_NewStringCopyN(cx, val.c_str(), val.length());
JS::RootedString str(cx, JS_NewStringCopyN(cx, val.c_str(), val.length()));
if (str)
ret.setString(str);
else
@ -339,7 +321,7 @@ template<> void ScriptInterface::ToJSVal<const wchar_t*>(JSContext* cx, JS::Muta
template<> void ScriptInterface::ToJSVal<const char*>(JSContext* cx, JS::MutableHandleValue ret, const char* const& val)
{
JSAutoRequest rq(cx);
JSString* str = JS_NewStringCopyZ(cx, val);
JS::RootedString str(cx, JS_NewStringCopyZ(cx, val));
if (str)
ret.setString(str);
else
@ -362,7 +344,7 @@ template<> void ScriptInterface::ToJSVal<CStr8>(JSContext* cx, JS::MutableHandle
template<typename T> static void ToJSVal_vector(JSContext* cx, JS::MutableHandleValue ret, const std::vector<T>& val)
{
JSAutoRequest rq(cx);
JSObject* obj = JS_NewArrayObject(cx, val.size(), NULL);
JS::RootedObject obj(cx, JS_NewArrayObject(cx, 0));
if (!obj)
{
ret.setUndefined();
@ -372,7 +354,7 @@ template<typename T> static void ToJSVal_vector(JSContext* cx, JS::MutableHandle
{
JS::RootedValue el(cx);
ScriptInterface::ToJSVal<T>(cx, &el, val[i]);
JS_SetElement(cx, obj, i, el.address());
JS_SetElement(cx, obj, i, el);
}
ret.setObject(*obj);
}
@ -380,7 +362,7 @@ template<typename T> static void ToJSVal_vector(JSContext* cx, JS::MutableHandle
template<typename T> static bool FromJSVal_vector(JSContext* cx, JS::HandleValue v, std::vector<T>& out)
{
JSAutoRequest rq(cx);
JSObject* obj;
JS::RootedObject obj(cx);
if (!v.isObject())
FAIL("Argument must be an array");
obj = &v.toObject();
@ -394,7 +376,7 @@ template<typename T> static bool FromJSVal_vector(JSContext* cx, JS::HandleValue
for (u32 i = 0; i < length; ++i)
{
JS::RootedValue el(cx);
if (!JS_GetElement(cx, obj, i, el.address()))
if (!JS_GetElement(cx, obj, i, &el))
FAIL("Failed to read array element");
T el2;
if (!ScriptInterface::FromJSVal<T>(cx, el, el2))
@ -422,7 +404,6 @@ VECTOR(u16)
VECTOR(std::string)
VECTOR(std::wstring)
VECTOR(CStr8)
VECTOR(CScriptValRooted)
class IComponent;

View File

@ -25,26 +25,15 @@
#include "scriptinterface/ScriptTypes.h"
// Ignore some harmless warnings
// Not all of these warnings can be disabled in older versions of GCC.
// The version checking was taken from the manual here:
// http://gcc.gnu.org/onlinedocs/
// Choose the version and navigate to "Options to Request or Suppress Warnings"
// or for some flags "Options Controlling C++ Dialect".
#if GCC_VERSION >= 402
# if GCC_VERSION >= 406 // store user flags
# pragma GCC diagnostic push
# endif
#if GCC_VERSION
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wunused-parameter"
# pragma GCC diagnostic ignored "-Wredundant-decls"
# pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
# if GCC_VERSION >= 407
# pragma GCC diagnostic ignored "-Wunused-local-typedefs" // caused by js/debug.h
# endif
#endif
#if MSC_VERSION
# pragma warning(push)
# pragma warning(disable:4100) // "unreferenced formal parameter"
# pragma warning(disable:4800) // "forcing value to bool 'true' or 'false' (performance warning)"
// reduce the warning level for the SpiderMonkey headers
# pragma warning(push, 1)
#endif
// Redefine signbit to fix build error in GCC
@ -53,22 +42,21 @@
#endif
#include "jsfriendapi.h"
#include "jsdbgapi.h"
#include "js/OldDebugAPI.h"
#include "js/GCAPI.h"
#include "js/StructuredClone.h"
#undef signbit
#if MSC_VERSION
# pragma warning(pop)
#endif
#if GCC_VERSION >= 402
#if GCC_VERSION
# pragma GCC diagnostic warning "-Wunused-parameter"
# pragma GCC diagnostic warning "-Wredundant-decls"
# pragma GCC diagnostic warning "-Wnon-virtual-dtor"
# if GCC_VERSION >= 406
// restore user flags (and we don't have to manually restore the warning levels for GCC >= 4.6)
# pragma GCC diagnostic pop
# endif
// restore user flags and re-enable the warnings disabled a few lines above
# pragma GCC diagnostic pop
#endif
#endif // INCLUDED_SCRIPTEXTRAHEADERS

View File

@ -21,7 +21,6 @@
#include "ScriptRuntime.h"
// #include "DebuggingServer.h" // JS debugger temporarily disabled during the SpiderMonkey upgrade (check trac ticket #2348 for details)
#include "ScriptStats.h"
#include "AutoRooters.h"
#include "lib/debug.h"
#include "lib/utf8.h"
@ -60,14 +59,21 @@ struct ScriptInterface_impl
~ScriptInterface_impl();
void Register(const char* name, JSNative fptr, uint nargs);
// Take care to keep this declaration before heap rooted members. Destructors of heap rooted
// members have to be called before the runtime destructor.
shared_ptr<ScriptRuntime> m_runtime;
JSContext* m_cx;
JSObject* m_glob; // global scope object
JS::PersistentRootedObject m_glob; // global scope object
JSCompartment* m_comp;
boost::rand48* m_rng;
JSObject* m_nativeScope; // native function scope object
JS::PersistentRootedObject m_nativeScope; // native function scope object
typedef std::map<ScriptInterface::CACHED_VAL, CScriptValRooted> ScriptValCache;
// TODO: we need DefPersistentRooted to work around a problem with JS::PersistentRooted<T>
// that is already solved in newer versions of SpiderMonkey (related to std::pair and
// and the copy constructor of PersistentRooted<T> taking a non-const reference).
// Switch this to PersistentRooted<T> when upgrading to a newer SpiderMonkey version than v31.
typedef std::map<ScriptInterface::CACHED_VAL, DefPersistentRooted<JS::Value> > ScriptValCache;
ScriptValCache m_ScriptValCache;
};
@ -78,9 +84,9 @@ JSClass global_class = {
"global", JSCLASS_GLOBAL_FLAGS,
JS_PropertyStub, JS_DeletePropertyStub,
JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, JS_ResolveStub,
JS_ConvertStub, NULL,
NULL, NULL, NULL, NULL
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub,
nullptr, nullptr, nullptr, nullptr,
JS_GlobalObjectTraceHook
};
void ErrorReporter(JSContext* cx, const char* message, JSErrorReport* report)
@ -99,7 +105,7 @@ void ErrorReporter(JSContext* cx, const char* message, JSErrorReport* report)
// If there is an exception, then print its stack trace
JS::RootedValue excn(cx);
if (JS_GetPendingException(cx, excn.address()) && excn.isObject())
if (JS_GetPendingException(cx, &excn) && excn.isObject())
{
JS::RootedObject excnObj(cx, &excn.toObject());
// TODO: this violates the docs ("The error reporter callback must not reenter the JSAPI.")
@ -110,7 +116,7 @@ void ErrorReporter(JSContext* cx, const char* message, JSErrorReport* report)
JS::RootedValue rval(cx);
const char dumpStack[] = "this.stack.trimRight().replace(/^/mg, ' ')"; // indent each line
if (JS_EvaluateScript(cx, excnObj, dumpStack, ARRAY_SIZE(dumpStack)-1, "(eval)", 1, rval.address()))
if (JS_EvaluateScript(cx, excnObj, dumpStack, ARRAY_SIZE(dumpStack)-1, "(eval)", 1, &rval))
{
std::string stackTrace;
if (ScriptInterface::FromJSVal(cx, rval, stackTrace))
@ -136,88 +142,92 @@ void ErrorReporter(JSContext* cx, const char* message, JSErrorReport* report)
// Functions in the global namespace:
JSBool print(JSContext* cx, uint argc, jsval* vp)
bool print(JSContext* cx, uint argc, jsval* vp)
{
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
for (uint i = 0; i < args.length(); ++i)
{
std::wstring str;
if (!ScriptInterface::FromJSVal(cx, args.handleAt(i), str))
return JS_FALSE;
if (!ScriptInterface::FromJSVal(cx, args[i], str))
return false;
debug_printf(L"%ls", str.c_str());
}
fflush(stdout);
args.rval().setUndefined();
return JS_TRUE;
return true;
}
JSBool logmsg(JSContext* cx, uint argc, jsval* vp)
bool logmsg(JSContext* cx, uint argc, jsval* vp)
{
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
if (args.length() < 1)
{
args.rval().setUndefined();
return JS_TRUE;
return true;
}
std::wstring str;
if (!ScriptInterface::FromJSVal(cx, args.handleAt(0), str))
return JS_FALSE;
if (!ScriptInterface::FromJSVal(cx, args[0], str))
return false;
LOGMESSAGE("%s", utf8_from_wstring(str));
args.rval().setUndefined();
return JS_TRUE;
return true;
}
JSBool warn(JSContext* cx, uint argc, jsval* vp)
bool warn(JSContext* cx, uint argc, jsval* vp)
{
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
if (args.length() < 1)
{
args.rval().setUndefined();
return JS_TRUE;
return true;
}
std::wstring str;
if (!ScriptInterface::FromJSVal(cx, args.handleAt(0), str))
return JS_FALSE;
if (!ScriptInterface::FromJSVal(cx, args[0], str))
return false;
LOGWARNING("%s", utf8_from_wstring(str));
args.rval().setUndefined();
return JS_TRUE;
return true;
}
JSBool error(JSContext* cx, uint argc, jsval* vp)
bool error(JSContext* cx, uint argc, jsval* vp)
{
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
if (args.length() < 1)
{
args.rval().setUndefined();
return JS_TRUE;
return true;
}
std::wstring str;
if (!ScriptInterface::FromJSVal(cx, args.handleAt(0), str))
return JS_FALSE;
if (!ScriptInterface::FromJSVal(cx, args[0], str))
return false;
LOGERROR("%s", utf8_from_wstring(str));
args.rval().setUndefined();
return JS_TRUE;
return true;
}
JSBool deepcopy(JSContext* cx, uint argc, jsval* vp)
bool deepcopy(JSContext* cx, uint argc, jsval* vp)
{
JSAutoRequest rq(cx);
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
if (args.length() < 1)
{
args.rval().setUndefined();
return JS_TRUE;
return true;
}
if (!JS_StructuredClone(cx, args[0], args.rval().address(), NULL, NULL))
return JS_FALSE;
JS::RootedValue ret(cx);
if (!JS_StructuredClone(cx, args[0], &ret, NULL, NULL))
return false;
return JS_TRUE;
args.rval().set(ret);
return true;
}
JSBool ProfileStart(JSContext* cx, uint argc, jsval* vp)
bool ProfileStart(JSContext* cx, uint argc, jsval* vp)
{
const char* name = "(ProfileStart)";
@ -225,8 +235,8 @@ JSBool ProfileStart(JSContext* cx, uint argc, jsval* vp)
if (args.length() >= 1)
{
std::string str;
if (!ScriptInterface::FromJSVal(cx, args.handleAt(0), str))
return JS_FALSE;
if (!ScriptInterface::FromJSVal(cx, args[0], str))
return false;
typedef boost::flyweight<
std::string,
@ -243,10 +253,10 @@ JSBool ProfileStart(JSContext* cx, uint argc, jsval* vp)
g_Profiler2.RecordRegionEnter(name);
args.rval().setUndefined();
return JS_TRUE;
return true;
}
JSBool ProfileStop(JSContext* UNUSED(cx), uint UNUSED(argc), jsval* vp)
bool ProfileStop(JSContext* UNUSED(cx), uint UNUSED(argc), jsval* vp)
{
JS::CallReceiver rec = JS::CallReceiverFromVp(vp);
if (CProfileManager::IsInitialised() && ThreadUtil::IsMainThread())
@ -255,7 +265,7 @@ JSBool ProfileStop(JSContext* UNUSED(cx), uint UNUSED(argc), jsval* vp)
g_Profiler2.RecordRegionLeave("(ProfileStop)");
rec.rval().setUndefined();
return JS_TRUE;
return true;
}
// Math override functions:
@ -276,15 +286,15 @@ static double generate_uniform_real(boost::rand48& rng, double min, double max)
}
}
JSBool Math_random(JSContext* cx, uint UNUSED(argc), jsval* vp)
bool Math_random(JSContext* cx, uint UNUSED(argc), jsval* vp)
{
JS::CallReceiver rec = JS::CallReceiverFromVp(vp);
double r;
if(!ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface->MathRandom(r))
return JS_FALSE;
return false;
rec.rval().setNumber(r);
return JS_TRUE;
return true;
}
} // anonymous namespace
@ -298,14 +308,14 @@ bool ScriptInterface::MathRandom(double& nbr)
}
ScriptInterface_impl::ScriptInterface_impl(const char* nativeScopeName, const shared_ptr<ScriptRuntime>& runtime) :
m_runtime(runtime)
m_runtime(runtime), m_glob(runtime->m_rt), m_nativeScope(runtime->m_rt)
{
bool ok;
m_cx = JS_NewContext(m_runtime->m_rt, STACK_CHUNK_SIZE);
ENSURE(m_cx);
JS_SetParallelCompilationEnabled(m_cx, true);
JS_SetParallelIonCompilationEnabled(m_runtime->m_rt, true);
// For GC debugging:
// JS_SetGCZeal(m_cx, 2);
@ -314,52 +324,43 @@ ScriptInterface_impl::ScriptInterface_impl(const char* nativeScopeName, const sh
JS_SetErrorReporter(m_cx, ErrorReporter);
u32 options = 0;
options |= JSOPTION_EXTRA_WARNINGS; // "warn on dubious practice"
// We use strict mode to encourage better coding practices and
// to get code that can be optimized better by Spidermonkey's JIT compiler.
options |= JSOPTION_STRICT_MODE;
options |= JSOPTION_VAROBJFIX; // "recommended" (fixes variable scoping)
JS_SetGlobalJitCompilerOption(m_runtime->m_rt, JSJITCOMPILER_ION_ENABLE, 1);
JS_SetGlobalJitCompilerOption(m_runtime->m_rt, JSJITCOMPILER_BASELINE_ENABLE, 1);
// Enable method JIT, unless script profiling/debugging is enabled (since profiling/debugging
// hooks are incompatible with the JIT)
// TODO: Verify what exactly is incompatible
if (!g_ScriptProfilingEnabled && !g_JSDebuggerEnabled)
{
options |= JSOPTION_BASELINE;
options |= JSOPTION_ION;
options |= JSOPTION_TYPE_INFERENCE;
options |= JSOPTION_COMPILE_N_GO;
// Some other JIT flags to experiment with:
//options |= JSOPTION_METHODJIT_ALWAYS;
}
JS_SetOptions(m_cx, options);
JSAutoRequest rq(m_cx);
JS::ContextOptionsRef(m_cx).setExtraWarnings(1)
.setWerror(0)
.setVarObjFix(1)
.setStrictMode(1);
JS::CompartmentOptions opt;
opt.setVersion(JSVERSION_LATEST);
m_glob = JS_NewGlobalObject(m_cx, &global_class, NULL, opt);
m_comp = JS_EnterCompartment(m_cx, m_glob);
JS_SetGlobalObject(m_cx, m_glob);
ok = JS_InitStandardClasses(m_cx, m_glob);
JSAutoRequest rq(m_cx);
JS::RootedObject globalRootedVal(m_cx, JS_NewGlobalObject(m_cx, &global_class, NULL, JS::OnNewGlobalHookOption::FireOnNewGlobalHook, opt));
m_comp = JS_EnterCompartment(m_cx, globalRootedVal);
ok = JS_InitStandardClasses(m_cx, globalRootedVal);
ENSURE(ok);
m_glob = globalRootedVal.get();
// Use the testing functions to globally enable gcPreserveCode. This brings quite a
// big performance improvement. In future SpiderMonkey versions, we should probably
// use the functions implemented here: https://bugzilla.mozilla.org/show_bug.cgi?id=1068697
JS::RootedObject testingFunctionsObj(m_cx, js::GetTestingFunctions(m_cx));
ENSURE(testingFunctionsObj);
JS::RootedValue ret(m_cx);
JS_CallFunctionName(m_cx, testingFunctionsObj, "gcPreserveCode", JS::HandleValueArray::empty(), &ret);
JS_DefineProperty(m_cx, m_glob, "global", JS::ObjectValue(*m_glob), NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY
JS_DefineProperty(m_cx, m_glob, "global", globalRootedVal, JSPROP_ENUMERATE | JSPROP_READONLY
| JSPROP_PERMANENT);
m_nativeScope = JS_DefineObject(m_cx, m_glob, nativeScopeName, NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY
| JSPROP_PERMANENT);
JS_DefineFunction(m_cx, m_glob, "print", ::print, 0, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
JS_DefineFunction(m_cx, m_glob, "log", ::logmsg, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
JS_DefineFunction(m_cx, m_glob, "warn", ::warn, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
JS_DefineFunction(m_cx, m_glob, "error", ::error, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
JS_DefineFunction(m_cx, m_glob, "deepcopy", ::deepcopy, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
JS_DefineFunction(m_cx, globalRootedVal, "print", ::print, 0, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
JS_DefineFunction(m_cx, globalRootedVal, "log", ::logmsg, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
JS_DefineFunction(m_cx, globalRootedVal, "warn", ::warn, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
JS_DefineFunction(m_cx, globalRootedVal, "error", ::error, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
JS_DefineFunction(m_cx, globalRootedVal, "deepcopy", ::deepcopy, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
Register("ProfileStart", ::ProfileStart, 1);
Register("ProfileStop", ::ProfileStop, 0);
@ -369,9 +370,6 @@ ScriptInterface_impl::ScriptInterface_impl(const char* nativeScopeName, const sh
ScriptInterface_impl::~ScriptInterface_impl()
{
// Important: this must come before JS_DestroyContext because CScriptValRooted needs the context to unroot the values!
m_ScriptValCache.clear();
m_runtime->UnRegisterContext(m_cx);
{
JSAutoRequest rq(m_cx);
@ -380,29 +378,18 @@ ScriptInterface_impl::~ScriptInterface_impl()
JS_DestroyContext(m_cx);
}
// Inside ScriptInterface.cpp directly to emphasize that it should only be used/needed internally and not from other code.
template<> bool ScriptInterface::FromJSVal<ScriptInterface::HandleWrapper>(JSContext* UNUSED(cx), JS::HandleValue v, ScriptInterface::HandleWrapper& out)
{
out.set(v);
return true;
}
void ScriptInterface_impl::Register(const char* name, JSNative fptr, uint nargs)
{
JSAutoRequest rq(m_cx);
JSFunction* func = JS_DefineFunction(m_cx, m_nativeScope, name, fptr, nargs, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
if (!func)
return;
if (g_ScriptProfilingEnabled)
{
// Store the function name in a slot, so we can pass it to the profiler.
// Use a flyweight std::string because we can't assume the caller has
// a correctly-aligned string and that its lifetime is long enough
typedef boost::flyweight<
std::string,
boost::flyweights::no_tracking
// can't use no_locking; Register might be called in threads
> LockedStringFlyweight;
LockedStringFlyweight fw(name);
JS_SetReservedSlot(JS_GetFunctionObject(func), 0, PRIVATE_TO_JSVAL((void*)fw.get().c_str()));
}
JS::RootedObject nativeScope(m_cx, m_nativeScope);
JS::RootedFunction func(m_cx, JS_DefineFunction(m_cx, nativeScope, name, fptr, nargs, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT));
}
ScriptInterface::ScriptInterface(const char* nativeScopeName, const char* debugName, const shared_ptr<ScriptRuntime>& runtime) :
@ -459,9 +446,9 @@ ScriptInterface::CxPrivate* ScriptInterface::GetScriptInterfaceAndCBData(JSConte
return pCxPrivate;
}
CScriptValRooted ScriptInterface::GetCachedValue(CACHED_VAL valueIdentifier)
JS::Value ScriptInterface::GetCachedValue(CACHED_VAL valueIdentifier)
{
return m->m_ScriptValCache[valueIdentifier];
return m->m_ScriptValCache[valueIdentifier].get();
}
@ -484,11 +471,12 @@ bool ScriptInterface::LoadGlobalScripts()
}
JSAutoRequest rq(m->m_cx);
jsval proto;
if (JS_GetProperty(m->m_cx, m->m_glob, "Vector2Dprototype", &proto))
m->m_ScriptValCache[CACHE_VECTOR2DPROTO] = CScriptValRooted(m->m_cx, proto);
if (JS_GetProperty(m->m_cx, m->m_glob, "Vector3Dprototype", &proto))
m->m_ScriptValCache[CACHE_VECTOR3DPROTO] = CScriptValRooted(m->m_cx, proto);
JS::RootedValue proto(m->m_cx);
JS::RootedObject global(m->m_cx, m->m_glob);
if (JS_GetProperty(m->m_cx, global, "Vector2Dprototype", &proto))
m->m_ScriptValCache[CACHE_VECTOR2DPROTO] = DefPersistentRooted<JS::Value>(GetJSRuntime(), proto);
if (JS_GetProperty(m->m_cx, global, "Vector3Dprototype", &proto))
m->m_ScriptValCache[CACHE_VECTOR3DPROTO] = DefPersistentRooted<JS::Value>(GetJSRuntime(), proto);
return true;
}
@ -496,10 +484,12 @@ bool ScriptInterface::ReplaceNondeterministicRNG(boost::rand48& rng)
{
JSAutoRequest rq(m->m_cx);
JS::RootedValue math(m->m_cx);
if (JS_GetProperty(m->m_cx, m->m_glob, "Math", math.address()) && math.isObject())
JS::RootedObject global(m->m_cx, m->m_glob);
if (JS_GetProperty(m->m_cx, global, "Math", &math) && math.isObject())
{
JSFunction* random = JS_DefineFunction(m->m_cx, &math.toObject(), "random", Math_random, 0,
JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
JS::RootedObject mathObj(m->m_cx, &math.toObject());
JS::RootedFunction random(m->m_cx, JS_DefineFunction(m->m_cx, mathObj, "random", Math_random, 0,
JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT));
if (random)
{
m->m_rng = &rng;
@ -531,14 +521,7 @@ shared_ptr<ScriptRuntime> ScriptInterface::GetRuntime() const
return m->m_runtime;
}
AutoGCRooter* ScriptInterface::ReplaceAutoGCRooter(AutoGCRooter* rooter)
{
AutoGCRooter* ret = m->m_runtime->m_rooter;
m->m_runtime->m_rooter = rooter;
return ret;
}
void ScriptInterface::CallConstructor(JS::HandleValue ctor, JS::AutoValueVector& argv, JS::MutableHandleValue out)
void ScriptInterface::CallConstructor(JS::HandleValue ctor, JS::HandleValueArray argv, JS::MutableHandleValue out)
{
JSAutoRequest rq(m->m_cx);
if (!ctor.isObject())
@ -548,11 +531,8 @@ void ScriptInterface::CallConstructor(JS::HandleValue ctor, JS::AutoValueVector&
return;
}
// Passing argc 0 and argv JSVAL_VOID causes a crash in mozjs24
if (argv.length() == 0)
out.setObjectOrNull(JS_New(m->m_cx, &ctor.toObject(), 0, NULL));
else
out.setObjectOrNull(JS_New(m->m_cx, &ctor.toObject(), argv.length(), argv.begin()));
JS::RootedObject ctorObj(m->m_cx, &ctor.toObject());
out.setObjectOrNull(JS_New(m->m_cx, ctorObj, argv));
}
void ScriptInterface::DefineCustomObjectType(JSClass *clasp, JSNative constructor, uint minArgs, JSPropertySpec *ps, JSFunctionSpec *fs, JSPropertySpec *static_ps, JSFunctionSpec *static_fs)
@ -566,22 +546,23 @@ void ScriptInterface::DefineCustomObjectType(JSClass *clasp, JSNative constructo
throw PSERROR_Scripting_DefineType_AlreadyExists();
}
JSObject * obj = JS_InitClass( m->m_cx, &GetGlobalObject().toObject(), 0,
JS::RootedObject global(m->m_cx, m->m_glob);
JS::RootedObject obj(m->m_cx, JS_InitClass(m->m_cx, global, JS::NullPtr(),
clasp,
constructor, minArgs, // Constructor, min args
ps, fs, // Properties, methods
static_ps, static_fs); // Constructor properties, methods
static_ps, static_fs)); // Constructor properties, methods
if (obj == NULL)
throw PSERROR_Scripting_DefineType_CreationFailed();
CustomType type;
type.m_Prototype = obj;
type.m_Prototype = DefPersistentRooted<JSObject*>(m->m_cx, obj);
type.m_Class = clasp;
type.m_Constructor = constructor;
m_CustomObjectTypes[typeName] = type;
m_CustomObjectTypes[typeName] = std::move(type);
}
JSObject* ScriptInterface::CreateCustomObject(const std::string & typeName)
@ -591,31 +572,31 @@ JSObject* ScriptInterface::CreateCustomObject(const std::string & typeName)
if (it == m_CustomObjectTypes.end())
throw PSERROR_Scripting_TypeDoesNotExist();
JS::RootedObject prototype(m->m_cx, (*it).second.m_Prototype);
return JS_NewObject(m->m_cx, (*it).second.m_Class, prototype, NULL);
JS::RootedObject prototype(m->m_cx, it->second.m_Prototype.get());
return JS_NewObject(m->m_cx, (*it).second.m_Class, prototype, JS::NullPtr());
}
bool ScriptInterface::CallFunctionVoid(JS::HandleValue val, const char* name)
{
JSAutoRequest rq(m->m_cx);
JS::RootedValue jsRet(m->m_cx);
return CallFunction_(val, name, 0, NULL, &jsRet);
return CallFunction_(val, name, JS::HandleValueArray::empty(), &jsRet);
}
bool ScriptInterface::CallFunction_(JS::HandleValue val, const char* name, uint argc, jsval* argv, JS::MutableHandleValue ret)
bool ScriptInterface::CallFunction_(JS::HandleValue val, const char* name, JS::HandleValueArray argv, JS::MutableHandleValue ret)
{
JSAutoRequest rq(m->m_cx);
JS::RootedObject obj(m->m_cx);
if (!JS_ValueToObject(m->m_cx, val, obj.address()) || !obj)
if (!JS_ValueToObject(m->m_cx, val, &obj) || !obj)
return false;
// Check that the named function actually exists, to avoid ugly JS error reports
// when calling an undefined value
JSBool found;
bool found;
if (!JS_HasProperty(m->m_cx, obj, name, &found) || !found)
return JS_FALSE;
return false;
bool ok = JS_CallFunctionName(m->m_cx, obj, name, argc, argv, ret.address());
bool ok = JS_CallFunctionName(m->m_cx, obj, name, argv, ret);
return ok;
}
@ -623,7 +604,7 @@ bool ScriptInterface::CallFunction_(JS::HandleValue val, const char* name, uint
jsval ScriptInterface::GetGlobalObject()
{
JSAutoRequest rq(m->m_cx);
return JS::ObjectValue(*JS_GetGlobalForScopeChain(m->m_cx));
return JS::ObjectValue(*JS::CurrentGlobalOrNull(m->m_cx));
}
JSClass* ScriptInterface::GetGlobalClass()
@ -631,22 +612,23 @@ JSClass* ScriptInterface::GetGlobalClass()
return &global_class;
}
bool ScriptInterface::SetGlobal_(const char* name, jsval value, bool replace)
bool ScriptInterface::SetGlobal_(const char* name, JS::HandleValue value, bool replace)
{
JSAutoRequest rq(m->m_cx);
JS::RootedObject global(m->m_cx, m->m_glob);
if (!replace)
{
JSBool found;
if (!JS_HasProperty(m->m_cx, m->m_glob, name, &found))
return JS_FALSE;
bool found;
if (!JS_HasProperty(m->m_cx, global, name, &found))
return false;
if (found)
{
JS_ReportError(m->m_cx, "SetGlobal \"%s\" called multiple times", name);
return JS_FALSE;
return false;
}
}
bool ok = JS_DefineProperty(m->m_cx, m->m_glob, name, value, NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY
bool ok = JS_DefineProperty(m->m_cx, global, name, value, JSPROP_ENUMERATE | JSPROP_READONLY
| JSPROP_PERMANENT);
return ok;
}
@ -664,7 +646,7 @@ bool ScriptInterface::SetProperty_(JS::HandleValue obj, const char* name, JS::Ha
return false;
JS::RootedObject object(m->m_cx, &obj.toObject());
if (! JS_DefineProperty(m->m_cx, object, name, value, NULL, NULL, attrs))
if (! JS_DefineProperty(m->m_cx, object, name, value, attrs))
return false;
return true;
}
@ -706,6 +688,33 @@ bool ScriptInterface::SetPropertyInt_(JS::HandleValue obj, int name, JS::HandleV
return true;
}
bool ScriptInterface::GetProperty(JS::HandleValue obj, const char* name, JS::MutableHandleValue out)
{
return GetProperty_(obj, name, out);
}
bool ScriptInterface::GetProperty(JS::HandleValue obj, const char* name, JS::MutableHandleObject out)
{
JSContext* cx = GetContext();
JSAutoRequest rq(cx);
JS::RootedValue val(cx);
if (!GetProperty_(obj, name, &val))
return false;
if (!val.isObject())
{
LOGERROR("GetProperty failed: trying to get an object, but the property is not an object!");
return false;
}
out.set(&val.toObject());
return true;
}
bool ScriptInterface::GetPropertyInt(JS::HandleValue obj, int name, JS::MutableHandleValue out)
{
return GetPropertyInt_(obj, name, out);
}
bool ScriptInterface::GetProperty_(JS::HandleValue obj, const char* name, JS::MutableHandleValue out)
{
JSAutoRequest rq(m->m_cx);
@ -713,7 +722,7 @@ bool ScriptInterface::GetProperty_(JS::HandleValue obj, const char* name, JS::Mu
return false;
JS::RootedObject object(m->m_cx, &obj.toObject());
if (!JS_GetProperty(m->m_cx, object, name, out.address()))
if (!JS_GetProperty(m->m_cx, object, name, out))
return false;
return true;
}
@ -721,11 +730,12 @@ bool ScriptInterface::GetProperty_(JS::HandleValue obj, const char* name, JS::Mu
bool ScriptInterface::GetPropertyInt_(JS::HandleValue obj, int name, JS::MutableHandleValue out)
{
JSAutoRequest rq(m->m_cx);
JS::RootedId nameId(m->m_cx, INT_TO_JSID(name));
if (!obj.isObject())
return false;
JS::RootedObject object(m->m_cx, &obj.toObject());
if (!JS_GetPropertyById(m->m_cx, object, INT_TO_JSID(name), out.address()))
if (!JS_GetPropertyById(m->m_cx, object, nameId, out))
return false;
return true;
}
@ -735,12 +745,12 @@ bool ScriptInterface::HasProperty(JS::HandleValue obj, const char* name)
// TODO: proper errorhandling
JSAutoRequest rq(m->m_cx);
if (!obj.isObject())
return JS_FALSE;
return false;
JS::RootedObject object(m->m_cx, &obj.toObject());
JSBool found;
bool found;
if (!JS_HasProperty(m->m_cx, object, name, &found))
return JS_FALSE;
return false;
return found;
}
@ -766,7 +776,7 @@ bool ScriptInterface::EnumeratePropertyNamesWithPrefix(JS::HandleValue objVal, c
{
JS::RootedId idp(m->m_cx);
JS::RootedValue val(m->m_cx);
if (! JS_NextProperty(m->m_cx, it, idp.address()) || ! JS_IdToValue(m->m_cx, idp, val.address()))
if (! JS_NextProperty(m->m_cx, it, idp.address()) || ! JS_IdToValue(m->m_cx, idp, &val))
return false;
if (val.isUndefined())
@ -790,7 +800,7 @@ bool ScriptInterface::EnumeratePropertyNamesWithPrefix(JS::HandleValue objVal, c
// Recurse up the prototype chain
JS::RootedObject prototype(m->m_cx);
if (JS_GetPrototype(m->m_cx, obj, prototype.address()))
if (JS_GetPrototype(m->m_cx, obj, &prototype))
{
JS::RootedValue prototypeVal(m->m_cx, JS::ObjectOrNullValue(prototype));
if (! EnumeratePropertyNamesWithPrefix(prototypeVal, prefix, out))
@ -800,12 +810,14 @@ bool ScriptInterface::EnumeratePropertyNamesWithPrefix(JS::HandleValue objVal, c
return true;
}
bool ScriptInterface::SetPrototype(JS::HandleValue obj, JS::HandleValue proto)
bool ScriptInterface::SetPrototype(JS::HandleValue objVal, JS::HandleValue protoVal)
{
JSAutoRequest rq(m->m_cx);
if (!obj.isObject() || !proto.isObject())
if (!objVal.isObject() || !protoVal.isObject())
return false;
return JS_SetPrototype(m->m_cx, &obj.toObject(), &proto.toObject());
JS::RootedObject obj(m->m_cx, &objVal.toObject());
JS::RootedObject proto(m->m_cx, &protoVal.toObject());
return JS_SetPrototype(m->m_cx, obj, proto);
}
bool ScriptInterface::FreezeObject(JS::HandleValue objVal, bool deep)
@ -824,46 +836,52 @@ bool ScriptInterface::FreezeObject(JS::HandleValue objVal, bool deep)
bool ScriptInterface::LoadScript(const VfsPath& filename, const std::string& code)
{
JSAutoRequest rq(m->m_cx);
JS::RootedObject global(m->m_cx, m->m_glob);
utf16string codeUtf16(code.begin(), code.end());
uint lineNo = 1;
// CompileOptions does not copy the contents of the filename string pointer.
// Passing a temporary string there will cause undefined behaviour, so we create a separate string to avoid the temporary.
std::string filenameStr(utf8_from_wstring(filename.string()));
JS::CompileOptions options(m->m_cx);
options.setFileAndLine(filenameStr.c_str(), lineNo);
options.setCompileAndGo(true);
JS::RootedFunction func(m->m_cx,
JS_CompileUCFunction(m->m_cx, m->m_glob, NULL, 0, NULL,
reinterpret_cast<const jschar*> (codeUtf16.c_str()), (uint)(codeUtf16.length()),
utf8_from_wstring(filename.string()).c_str(), lineNo)
JS_CompileUCFunction(m->m_cx, global, NULL, 0, NULL,
reinterpret_cast<const jschar*> (codeUtf16.c_str()), (uint)(codeUtf16.length()), options)
);
if (!func)
return false;
JS::RootedValue val(m->m_cx);
bool ok = JS_CallFunction(m->m_cx, NULL, func, 0, NULL, val.address());
return ok;
JS::RootedValue rval(m->m_cx);
return JS_CallFunction(m->m_cx, JS::NullPtr(), func, JS::HandleValueArray::empty(), &rval);
}
shared_ptr<ScriptRuntime> ScriptInterface::CreateRuntime(int runtimeSize, int heapGrowthBytesGCTrigger)
shared_ptr<ScriptRuntime> ScriptInterface::CreateRuntime(shared_ptr<ScriptRuntime> parentRuntime, int runtimeSize, int heapGrowthBytesGCTrigger)
{
return shared_ptr<ScriptRuntime>(new ScriptRuntime(runtimeSize, heapGrowthBytesGCTrigger));
return shared_ptr<ScriptRuntime>(new ScriptRuntime(parentRuntime, runtimeSize, heapGrowthBytesGCTrigger));
}
bool ScriptInterface::LoadGlobalScript(const VfsPath& filename, const std::wstring& code)
{
JSAutoRequest rq(m->m_cx);
JS::RootedObject global(m->m_cx, m->m_glob);
utf16string codeUtf16(code.begin(), code.end());
uint lineNo = 1;
jsval rval;
bool ok = JS_EvaluateUCScript(m->m_cx, m->m_glob,
JS::RootedValue rval(m->m_cx);
return JS_EvaluateUCScript(m->m_cx, global,
reinterpret_cast<const jschar*> (codeUtf16.c_str()), (uint)(codeUtf16.length()),
utf8_from_wstring(filename.string()).c_str(), lineNo, &rval);
return ok;
}
bool ScriptInterface::LoadGlobalScriptFile(const VfsPath& path)
{
JSAutoRequest rq(m->m_cx);
JS::RootedObject global(m->m_cx, m->m_glob);
if (!VfsFileExists(path))
{
LOGERROR("File '%s' does not exist", path.string8());
@ -885,12 +903,10 @@ bool ScriptInterface::LoadGlobalScriptFile(const VfsPath& path)
utf16string codeUtf16(code.begin(), code.end());
uint lineNo = 1;
jsval rval;
bool ok = JS_EvaluateUCScript(m->m_cx, m->m_glob,
JS::RootedValue rval(m->m_cx);
return JS_EvaluateUCScript(m->m_cx, global,
reinterpret_cast<const jschar*> (codeUtf16.c_str()), (uint)(codeUtf16.length()),
utf8_from_wstring(path.string()).c_str(), lineNo, &rval);
return ok;
}
bool ScriptInterface::Eval(const char* code)
@ -903,19 +919,19 @@ bool ScriptInterface::Eval(const char* code)
bool ScriptInterface::Eval_(const char* code, JS::MutableHandleValue rval)
{
JSAutoRequest rq(m->m_cx);
JS::RootedObject global(m->m_cx, m->m_glob);
utf16string codeUtf16(code, code+strlen(code));
bool ok = JS_EvaluateUCScript(m->m_cx, m->m_glob, (const jschar*)codeUtf16.c_str(), (uint)codeUtf16.length(), "(eval)", 1, rval.address());
return ok;
return JS_EvaluateUCScript(m->m_cx, global, (const jschar*)codeUtf16.c_str(), (uint)codeUtf16.length(), "(eval)", 1, rval);
}
bool ScriptInterface::Eval_(const wchar_t* code, JS::MutableHandleValue rval)
{
JSAutoRequest rq(m->m_cx);
JS::RootedObject global(m->m_cx, m->m_glob);
utf16string codeUtf16(code, code+wcslen(code));
bool ok = JS_EvaluateUCScript(m->m_cx, m->m_glob, (const jschar*)codeUtf16.c_str(), (uint)codeUtf16.length(), "(eval)", 1, rval.address());
return ok;
return JS_EvaluateUCScript(m->m_cx, global, (const jschar*)codeUtf16.c_str(), (uint)codeUtf16.length(), "(eval)", 1, rval);
}
bool ScriptInterface::ParseJSON(const std::string& string_utf8, JS::MutableHandleValue out)
@ -931,7 +947,7 @@ bool ScriptInterface::ParseJSON(const std::string& string_utf8, JS::MutableHandl
return false;
JS::RootedValue exc(m->m_cx);
if (!JS_GetPendingException(m->m_cx, exc.address()))
if (!JS_GetPendingException(m->m_cx, &exc))
return false;
JS_ClearPendingException(m->m_cx);
@ -941,7 +957,7 @@ bool ScriptInterface::ParseJSON(const std::string& string_utf8, JS::MutableHandl
JS::RootedValue rval(m->m_cx);
JS::RootedObject excObj(m->m_cx, &exc.toObject());
if (!JS_CallFunctionName(m->m_cx, excObj, "toString", 0, NULL, rval.address()))
if (!JS_CallFunctionName(m->m_cx, excObj, "toString", JS::HandleValueArray::empty(), &rval))
return false;
std::wstring error;
@ -976,14 +992,14 @@ void ScriptInterface::ReadJSONFile(const VfsPath& path, JS::MutableHandleValue o
struct Stringifier
{
static JSBool callback(const jschar* buf, u32 len, void* data)
static bool callback(const jschar* 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 JS_TRUE;
return true;
}
std::stringstream stream;
@ -991,11 +1007,11 @@ struct Stringifier
struct StringifierW
{
static JSBool callback(const jschar* buf, u32 len, void* data)
static bool callback(const jschar* buf, u32 len, void* data)
{
utf16string str(buf, buf+len);
static_cast<StringifierW*>(data)->stream << std::wstring(str.begin(), str.end());
return JS_TRUE;
return true;
}
std::wstringstream stream;
@ -1008,7 +1024,7 @@ std::string ScriptInterface::StringifyJSON(JS::MutableHandleValue obj, bool inde
JSAutoRequest rq(m->m_cx);
Stringifier str;
JS::RootedValue indentVal(m->m_cx, indent ? JS::Int32Value(2) : JS::UndefinedValue());
if (!JS_Stringify(m->m_cx, obj.address(), NULL, indentVal, &Stringifier::callback, &str))
if (!JS_Stringify(m->m_cx, obj, JS::NullPtr(), indentVal, &Stringifier::callback, &str))
{
JS_ClearPendingException(m->m_cx);
LOGERROR("StringifyJSON failed");
@ -1031,11 +1047,12 @@ std::wstring ScriptInterface::ToString(JS::MutableHandleValue obj, bool pretty)
if (pretty)
{
StringifierW str;
JS::RootedValue indentVal(m->m_cx, JS::Int32Value(2));
// Temporary disable the error reporter, so we don't print complaints about cyclic values
JSErrorReporter er = JS_SetErrorReporter(m->m_cx, NULL);
JSBool ok = JS_Stringify(m->m_cx, obj.address(), NULL, JS::NumberValue(2), &StringifierW::callback, &str);
bool ok = JS_Stringify(m->m_cx, obj, JS::NullPtr(), indentVal, &StringifierW::callback, &str);
// Restore error reporter
JS_SetErrorReporter(m->m_cx, er);
@ -1062,7 +1079,7 @@ void ScriptInterface::ReportError(const char* msg)
// script callers will be unable to catch anything. So use JS_SetPendingException
// to make sure there really is a script-level exception. But just set it to undefined
// because there's not much value yet in throwing a real exception object.
JS_SetPendingException(m->m_cx, JSVAL_VOID);
JS_SetPendingException(m->m_cx, JS::UndefinedHandleValue);
// And report the actual error
JS_ReportError(m->m_cx, "%s", msg);
@ -1075,7 +1092,7 @@ bool ScriptInterface::IsExceptionPending(JSContext* cx)
return JS_IsExceptionPending(cx) ? true : false;
}
JSClass* ScriptInterface::GetClass(JS::HandleObject obj)
const JSClass* ScriptInterface::GetClass(JS::HandleObject obj)
{
return JS_GetClass(obj);
}
@ -1125,7 +1142,7 @@ ScriptInterface::StructuredClone::StructuredClone() :
ScriptInterface::StructuredClone::~StructuredClone()
{
if (m_Data)
JS_ClearStructuredClone(m_Data, m_Size);
JS_ClearStructuredClone(m_Data, m_Size, NULL, NULL);
}
shared_ptr<ScriptInterface::StructuredClone> ScriptInterface::WriteStructuredClone(JS::HandleValue v)
@ -1133,7 +1150,7 @@ shared_ptr<ScriptInterface::StructuredClone> ScriptInterface::WriteStructuredClo
JSAutoRequest rq(m->m_cx);
u64* data = NULL;
size_t nbytes = 0;
if (!JS_WriteStructuredClone(m->m_cx, v, &data, &nbytes, NULL, NULL, JSVAL_VOID))
if (!JS_WriteStructuredClone(m->m_cx, v, &data, &nbytes, NULL, NULL, JS::UndefinedHandleValue))
{
debug_warn(L"Writing a structured clone with JS_WriteStructuredClone failed!");
return shared_ptr<StructuredClone>();
@ -1148,5 +1165,5 @@ shared_ptr<ScriptInterface::StructuredClone> ScriptInterface::WriteStructuredClo
void ScriptInterface::ReadStructuredClone(const shared_ptr<ScriptInterface::StructuredClone>& ptr, JS::MutableHandleValue ret)
{
JSAutoRequest rq(m->m_cx);
JS_ReadStructuredClone(m->m_cx, ptr->m_Data, ptr->m_Size, JS_STRUCTURED_CLONE_VERSION, ret.address(), NULL, NULL);
JS_ReadStructuredClone(m->m_cx, ptr->m_Data, ptr->m_Size, JS_STRUCTURED_CLONE_VERSION, ret, NULL, NULL);
}

View File

@ -45,8 +45,6 @@ ERROR_SUBGROUP(Scripting, DefineType);
ERROR_TYPE(Scripting_DefineType, AlreadyExists);
ERROR_TYPE(Scripting_DefineType, CreationFailed);
class AutoGCRooter;
// Set the maximum number of function arguments that can be handled
// (This should be as small as possible (for compiler efficiency),
// but as large as necessary for all wrapped functions)
@ -85,7 +83,7 @@ public:
* Each runtime should only ever be used on a single thread.
* @param runtimeSize Maximum size in bytes of the new runtime
*/
static shared_ptr<ScriptRuntime> CreateRuntime(int runtimeSize = DEFAULT_RUNTIME_SIZE,
static shared_ptr<ScriptRuntime> CreateRuntime(shared_ptr<ScriptRuntime> parentRuntime = shared_ptr<ScriptRuntime>(), int runtimeSize = DEFAULT_RUNTIME_SIZE,
int heapGrowthBytesGCTrigger = DEFAULT_HEAP_GROWTH_BYTES_GCTRIGGER);
@ -126,7 +124,7 @@ public:
bool LoadGlobalScripts();
enum CACHED_VAL { CACHE_VECTOR2DPROTO, CACHE_VECTOR3DPROTO };
CScriptValRooted GetCachedValue(CACHED_VAL valueIdentifier);
JS::Value GetCachedValue(CACHED_VAL valueIdentifier);
/**
* Replace the default JS random number geenrator with a seeded, network-sync'd one.
@ -139,7 +137,7 @@ public:
* @param argv Constructor arguments
* @param out The new object; On error an error message gets logged and out is Null (out.isNull() == true).
*/
void CallConstructor(JS::HandleValue ctor, JS::AutoValueVector& argv, JS::MutableHandleValue out);
void CallConstructor(JS::HandleValue ctor, JS::HandleValueArray argv, JS::MutableHandleValue out);
/**
* Call the named property on the given object, with void return type and 0 arguments
@ -205,24 +203,12 @@ public:
*/
template<typename T>
bool GetProperty(JS::HandleValue obj, const char* name, T& out);
/**
* Get the named property of the given object.
* This overload takes JS::Rooted<T>* and converts it to JS::MutableHandle<T> in the function body because implicit
* conversion is not supported for templates.
* It's used in the case where a JS::Rooted<T> gets created inside the same function and then passed to GetProperty as
* |out| using the & operator.
*/
template<typename T>
bool GetProperty(JS::HandleValue obj, const char* name, JS::Rooted<T>* out);
/**
* Get the named property of the given object.
* This overload gets used in the case when you don't need conversion from a JS::Rooted<T>* and pass JS::MutableHandle<T>
* to GetProperty as |out| parameter directly (usually when you get it as a function parameter).
*/
template<typename T>
bool GetProperty(JS::HandleValue obj, const char* name, JS::MutableHandle<T> out);
bool GetProperty(JS::HandleValue obj, const char* name, JS::MutableHandleValue out);
bool GetProperty(JS::HandleValue obj, const char* name, JS::MutableHandleObject out);
/**
* Get the integer-named property on the given object.
@ -230,23 +216,10 @@ public:
template<typename T>
bool GetPropertyInt(JS::HandleValue obj, int name, T& out);
/**
* Get the integer-named property on the given object.
* This overload takes JS::Rooted<T>* and converts it to JS::MutableHandle<T> in the function body because implicit
* conversion is not supported for templates.
* It's used in the case where a JS::Rooted<T> gets created inside the same function and then passed to GetPropertyInt as
* |out| using the & operator.
*/
template<typename T>
bool GetPropertyInt(JS::HandleValue obj, int name, JS::Rooted<T>* out);
/**
* Get the named property of the given object.
* This overload gets used in the case when you don't need conversion from a JS::Rooted<T>* and pass JS::MutableHandle<T>
* to GetPropertyInt as |out| parameter directly (usually when you get it as a function parameter).
*/
template<typename T>
bool GetPropertyInt(JS::HandleValue obj, int name, JS::MutableHandle<T> out);
bool GetPropertyInt(JS::HandleValue obj, int name, JS::MutableHandleValue out);
/**
* Check the named property has been defined on the given object.
@ -287,7 +260,7 @@ public:
/**
* Report the given error message through the JS error reporting mechanism,
* and throw a JS exception. (Callers can check IsPendingException, and must
* return JS_FALSE in that case to propagate the exception.)
* return false in that case to propagate the exception.)
*/
void ReportError(const char* msg);
@ -335,8 +308,6 @@ public:
*/
template<typename T> static void ToJSVal(JSContext* cx, JS::MutableHandleValue ret, T const& val);
AutoGCRooter* ReplaceAutoGCRooter(AutoGCRooter* rooter);
/**
* Dump some memory heap debugging information to stderr.
*/
@ -391,31 +362,46 @@ public:
* because "conversions" from JS::HandleValue to JS::MutableHandleValue are unusual and should not happen "by accident".
*/
template <typename T>
void AssignOrToJSVal(JS::MutableHandleValue handle, const T& a);
static void AssignOrToJSVal(JSContext* cx, JS::MutableHandleValue handle, const T& a);
/**
* The same as AssignOrToJSVal, but also allows JS::Value for T.
* In most cases it's not safe to use the plain (unrooted) JS::Value type, but this can happen quite
* easily with template functions. The idea is that the linker prints an error if AssignOrToJSVal is
* used with JS::Value. If the specialization for JS::Value should be allowed, you can use this
* "unrooted" version of AssignOrToJSVal.
*/
template <typename T>
static void AssignOrToJSValUnrooted(JSContext* cx, JS::MutableHandleValue handle, const T& a)
{
AssignOrToJSVal(cx, handle, a);
}
private:
bool CallFunction_(JS::HandleValue val, const char* name, uint argc, jsval* argv, JS::MutableHandleValue ret);
bool CallFunction_(JS::HandleValue val, const char* name, JS::HandleValueArray argv, JS::MutableHandleValue ret);
bool Eval_(const char* code, JS::MutableHandleValue ret);
bool Eval_(const wchar_t* code, JS::MutableHandleValue ret);
bool SetGlobal_(const char* name, jsval value, bool replace);
bool SetGlobal_(const char* name, JS::HandleValue value, bool replace);
bool SetProperty_(JS::HandleValue obj, const char* name, JS::HandleValue value, bool readonly, bool enumerate);
bool SetProperty_(JS::HandleValue obj, const wchar_t* name, JS::HandleValue value, bool readonly, bool enumerate);
bool SetPropertyInt_(JS::HandleValue obj, int name, JS::HandleValue value, bool readonly, bool enumerate);
bool GetProperty_(JS::HandleValue obj, const char* name, JS::MutableHandleValue out);
bool GetPropertyInt_(JS::HandleValue obj, int name, JS::MutableHandleValue value);
static bool IsExceptionPending(JSContext* cx);
static JSClass* GetClass(JS::HandleObject obj);
static const JSClass* GetClass(JS::HandleObject obj);
static void* GetPrivate(JS::HandleObject obj);
class CustomType
struct CustomType
{
public:
JSObject* m_Prototype;
DefPersistentRooted<JSObject*> m_Prototype;
JSClass* m_Class;
JSNative m_Constructor;
};
void Register(const char* name, JSNative fptr, size_t nargs);
// Take care to keep this declaration before heap rooted members. Destructors of heap rooted
// members have to be called before the runtime destructor.
std::auto_ptr<ScriptInterface_impl> m;
boost::rand48* m_rng;
@ -443,25 +429,31 @@ public:
#include "NativeWrapperDefns.h"
template<typename T>
inline void ScriptInterface::AssignOrToJSVal(JS::MutableHandleValue handle, const T& a)
inline void ScriptInterface::AssignOrToJSVal(JSContext* cx, JS::MutableHandleValue handle, const T& a)
{
ToJSVal(GetContext(), handle, a);
ToJSVal(cx, handle, a);
}
template<>
inline void ScriptInterface::AssignOrToJSVal<JS::RootedValue>(JS::MutableHandleValue handle, const JS::RootedValue& a)
inline void ScriptInterface::AssignOrToJSVal<JS::PersistentRootedValue>(JSContext* UNUSED(cx), JS::MutableHandleValue handle, const JS::PersistentRootedValue& a)
{
handle.set(a);
}
template<>
inline void ScriptInterface::AssignOrToJSVal<JS::RootedValue>(JSContext* UNUSED(cx), JS::MutableHandleValue handle, const JS::RootedValue& a)
{
handle.set(a);
}
template <>
inline void ScriptInterface::AssignOrToJSVal<JS::HandleValue>(JS::MutableHandleValue handle, const JS::HandleValue& a)
inline void ScriptInterface::AssignOrToJSVal<JS::HandleValue>(JSContext* UNUSED(cx), JS::MutableHandleValue handle, const JS::HandleValue& a)
{
handle.set(a);
}
template <>
inline void ScriptInterface::AssignOrToJSVal<JS::Value>(JS::MutableHandleValue handle, const JS::Value& a)
inline void ScriptInterface::AssignOrToJSValUnrooted<JS::Value>(JSContext* UNUSED(cx), JS::MutableHandleValue handle, const JS::Value& a)
{
handle.set(a);
}
@ -474,8 +466,8 @@ bool ScriptInterface::CallFunctionVoid(JS::HandleValue val, const char* name, co
JS::RootedValue jsRet(cx);
JS::AutoValueVector argv(cx);
argv.resize(1);
AssignOrToJSVal(argv.handleAt(0), a0);
return CallFunction_(val, name, 1, argv.begin(), &jsRet);
AssignOrToJSVal(cx, argv.handleAt(0), a0);
return CallFunction_(val, name, argv, &jsRet);
}
template<typename T0, typename T1>
@ -486,9 +478,9 @@ bool ScriptInterface::CallFunctionVoid(JS::HandleValue val, const char* name, co
JS::RootedValue jsRet(cx);
JS::AutoValueVector argv(cx);
argv.resize(2);
AssignOrToJSVal(argv.handleAt(0), a0);
AssignOrToJSVal(argv.handleAt(1), a1);
return CallFunction_(val, name, 2, argv.begin(), &jsRet);
AssignOrToJSVal(cx, argv.handleAt(0), a0);
AssignOrToJSVal(cx, argv.handleAt(1), a1);
return CallFunction_(val, name, argv, &jsRet);
}
template<typename T0, typename T1, typename T2>
@ -499,10 +491,10 @@ bool ScriptInterface::CallFunctionVoid(JS::HandleValue val, const char* name, co
JS::RootedValue jsRet(cx);
JS::AutoValueVector argv(cx);
argv.resize(3);
AssignOrToJSVal(argv.handleAt(0), a0);
AssignOrToJSVal(argv.handleAt(1), a1);
AssignOrToJSVal(argv.handleAt(2), a2);
return CallFunction_(val, name, 3, argv.begin(), &jsRet);
AssignOrToJSVal(cx, argv.handleAt(0), a0);
AssignOrToJSVal(cx, argv.handleAt(1), a1);
AssignOrToJSVal(cx, argv.handleAt(2), a2);
return CallFunction_(val, name, argv, &jsRet);
}
template<typename T>
@ -510,7 +502,7 @@ bool ScriptInterface::SetGlobal(const char* name, const T& value, bool replace)
{
JSAutoRequest rq(GetContext());
JS::RootedValue val(GetContext());
ToJSVal(GetContext(), &val, value);
AssignOrToJSVal(GetContext(), &val, value);
return SetGlobal_(name, val, replace);
}
@ -519,7 +511,7 @@ bool ScriptInterface::SetProperty(JS::HandleValue obj, const char* name, const T
{
JSAutoRequest rq(GetContext());
JS::RootedValue val(GetContext());
AssignOrToJSVal(&val, value);
AssignOrToJSVal(GetContext(), &val, value);
return SetProperty_(obj, name, val, readonly, enumerate);
}
@ -528,7 +520,7 @@ bool ScriptInterface::SetProperty(JS::HandleValue obj, const wchar_t* name, cons
{
JSAutoRequest rq(GetContext());
JS::RootedValue val(GetContext());
AssignOrToJSVal(&val, value);
AssignOrToJSVal(GetContext(), &val, value);
return SetProperty_(obj, name, val, readonly, enumerate);
}
@ -537,7 +529,7 @@ bool ScriptInterface::SetPropertyInt(JS::HandleValue obj, int name, const T& val
{
JSAutoRequest rq(GetContext());
JS::RootedValue val(GetContext());
AssignOrToJSVal(&val, value);
AssignOrToJSVal(GetContext(), &val, value);
return SetPropertyInt_(obj, name, val, readonly, enumerate);
}
@ -552,23 +544,6 @@ bool ScriptInterface::GetProperty(JS::HandleValue obj, const char* name, T& out)
return FromJSVal(cx, val, out);
}
template<typename T>
bool ScriptInterface::GetProperty(JS::HandleValue obj, const char* name, JS::Rooted<T>* out)
{
JS::MutableHandle<T> handleOut(out);
if (! GetProperty_(obj, name, handleOut))
return false;
return true;
}
template<typename T>
bool ScriptInterface::GetProperty(JS::HandleValue obj, const char* name, JS::MutableHandle<T> out)
{
if (! GetProperty_(obj, name, out))
return false;
return true;
}
template<typename T>
bool ScriptInterface::GetPropertyInt(JS::HandleValue obj, int name, T& out)
{
@ -579,23 +554,6 @@ bool ScriptInterface::GetPropertyInt(JS::HandleValue obj, int name, T& out)
return FromJSVal(GetContext(), val, out);
}
template<typename T>
bool ScriptInterface::GetPropertyInt(JS::HandleValue obj, int name, JS::Rooted<T>* out)
{
JS::MutableHandle<T> handleOut(out);
if (! GetPropertyInt_(obj, name, handleOut))
return false;
return true;
}
template<typename T>
bool ScriptInterface::GetPropertyInt(JS::HandleValue obj, int name, JS::MutableHandle<T> out)
{
if (! GetPropertyInt_(obj, name, out))
return false;
return true;
}
template<typename CHAR>
bool ScriptInterface::Eval(const CHAR* code, JS::MutableHandleValue ret)
{

View File

@ -19,7 +19,6 @@
#include "ScriptRuntime.h"
#include "AutoRooters.h"
#include "ps/GameSetup/Config.h"
#include "ps/Profile.h"
@ -89,20 +88,45 @@ void GCSliceCallbackHook(JSRuntime* UNUSED(rt), JS::GCProgress progress, const J
#endif
}
ScriptRuntime::ScriptRuntime(int runtimeSize, int heapGrowthBytesGCTrigger):
m_rooter(NULL),
void ScriptRuntime::GCCallback(JSRuntime* UNUSED(rt), JSGCStatus status, void *data)
{
if (status == JSGC_END)
reinterpret_cast<ScriptRuntime*>(data)->GCCallbackMember();
}
void ScriptRuntime::GCCallbackMember()
{
m_FinalizationListObjectIdCache.clear();
}
void ScriptRuntime::AddDeferredFinalizationObject(const std::shared_ptr<void>& obj)
{
m_FinalizationListObjectIdCache.push_back(obj);
}
bool ScriptRuntime::m_Initialized = false;
ScriptRuntime::ScriptRuntime(shared_ptr<ScriptRuntime> parentRuntime, int runtimeSize, int heapGrowthBytesGCTrigger):
m_LastGCBytes(0),
m_LastGCCheck(0.0f),
m_HeapGrowthBytesGCTrigger(heapGrowthBytesGCTrigger),
m_RuntimeSize(runtimeSize)
{
m_rt = JS_NewRuntime(runtimeSize, JS_USE_HELPER_THREADS);
if (!m_Initialized)
{
ENSURE(JS_Init());
m_Initialized = true;
}
JSRuntime* parentJSRuntime = parentRuntime ? parentRuntime->m_rt : nullptr;
m_rt = JS_NewRuntime(runtimeSize, JS_USE_HELPER_THREADS, parentJSRuntime);
ENSURE(m_rt); // TODO: error handling
JS_SetNativeStackQuota(m_rt, 128 * sizeof(size_t) * 1024);
if (g_ScriptProfilingEnabled)
{
// Execute and call hooks are disabled if the runtime debug mode is disabled
JS_SetRuntimeDebugMode(m_rt, true);
// Profiler isn't thread-safe, so only enable this on the main thread
if (ThreadUtil::IsMainThread())
{
@ -115,6 +139,7 @@ ScriptRuntime::ScriptRuntime(int runtimeSize, int heapGrowthBytesGCTrigger):
}
JS::SetGCSliceCallback(m_rt, GCSliceCallbackHook);
JS_SetGCCallback(m_rt, ScriptRuntime::GCCallback, this);
JS_SetGCParameter(m_rt, JSGC_MAX_MALLOC_BYTES, m_RuntimeSize);
JS_SetGCParameter(m_rt, JSGC_MAX_BYTES, m_RuntimeSize);
@ -124,17 +149,16 @@ ScriptRuntime::ScriptRuntime(int runtimeSize, int heapGrowthBytesGCTrigger):
// 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_AddExtraGCRootsTracer(m_rt, jshook_trace, this);
m_dummyContext = JS_NewContext(m_rt, STACK_CHUNK_SIZE);
ENSURE(m_dummyContext);
}
ScriptRuntime::~ScriptRuntime()
{
JS_RemoveExtraGCRootsTracer(m_rt, jshook_trace, this);
JS_DestroyContext(m_dummyContext);
JS_SetGCCallback(m_rt, nullptr, nullptr);
JS_DestroyRuntime(m_rt);
ENSURE(m_FinalizationListObjectIdCache.empty() && "Leak: Removing callback while some objects still aren't finalized!");
}
void ScriptRuntime::RegisterContext(JSContext* cx)
@ -215,10 +239,20 @@ void ScriptRuntime::MaybeIncrementalGC(double delay)
}
else
{
if (gcBytes > m_RuntimeSize * 0.75)
{
ShrinkingGC();
#if GC_DEBUG_PRINT
printf("Running full GC because gcBytes > m_RuntimeSize / 2. \n");
printf("Running shrinking GC because gcBytes > m_RuntimeSize * 0.75. \n");
#endif
JS_GC(m_rt);
}
else
{
#if GC_DEBUG_PRINT
printf("Running full GC because gcBytes > m_RuntimeSize / 2. \n");
#endif
JS_GC(m_rt);
}
}
}
else
@ -237,7 +271,15 @@ void ScriptRuntime::MaybeIncrementalGC(double delay)
}
}
void* ScriptRuntime::jshook_script(JSContext* UNUSED(cx), JSAbstractFramePtr UNUSED(fp), bool UNUSED(isConstructing), JSBool before, JSBool* UNUSED(ok), void* closure)
void ScriptRuntime::ShrinkingGC()
{
JS_SetGCParameter(m_rt, JSGC_MODE, JSGC_MODE_COMPARTMENT);
JS::PrepareForFullGC(m_rt);
JS::ShrinkingGC(m_rt, JS::gcreason::REFRESH_FRAME);
JS_SetGCParameter(m_rt, JSGC_MODE, JSGC_MODE_INCREMENTAL);
}
void* ScriptRuntime::jshook_script(JSContext* UNUSED(cx), JSAbstractFramePtr UNUSED(fp), bool UNUSED(isConstructing), bool before, bool* UNUSED(ok), void* closure)
{
if (before)
g_Profiler.StartScript("script invocation");
@ -247,15 +289,17 @@ void* ScriptRuntime::jshook_script(JSContext* UNUSED(cx), JSAbstractFramePtr UNU
return closure;
}
void* ScriptRuntime::jshook_function(JSContext* cx, JSAbstractFramePtr fp, bool UNUSED(isConstructing), JSBool before, JSBool* UNUSED(ok), void* closure)
void* ScriptRuntime::jshook_function(JSContext* cx, JSAbstractFramePtr fp, bool UNUSED(isConstructing), bool before, bool* UNUSED(ok), void* closure)
{
JSAutoRequest rq(cx);
if (!before)
{
g_Profiler.Stop();
return closure;
}
JSFunction* fn = fp.maybeFun();
JS::RootedFunction fn(cx, fp.maybeFun());
if (!fn)
{
g_Profiler.StartScript("(function)");
@ -263,7 +307,7 @@ void* ScriptRuntime::jshook_function(JSContext* cx, JSAbstractFramePtr fp, bool
}
// Try to get the name of non-anonymous functions
JSString* name = JS_GetFunctionId(fn);
JS::RootedString name(cx, JS_GetFunctionId(fn));
if (name)
{
char* chars = JS_EncodeString(cx, name);
@ -275,29 +319,22 @@ void* ScriptRuntime::jshook_function(JSContext* cx, JSAbstractFramePtr fp, bool
}
}
// No name - compute from the location instead
JSScript* script;
uint lineno;
JS_DescribeScriptedCaller(cx, &script, &lineno);
ENSURE(script == fp.script());
ScriptLocation loc = { cx, fp.script(), JS_LineNumberToPC(cx, script, lineno) };
g_Profiler.StartScript(LocFlyweight(loc).get().name.c_str());
// No name - use fileName and line instead
JS::AutoFilename fileName;
unsigned lineno;
JS::DescribeScriptedCaller(cx, &fileName, &lineno);
std::stringstream ss;
ss << "(" << fileName.get() << ":" << lineno << ")";
g_Profiler.StartScript(StringFlyweight(ss.str()).get().c_str());
return closure;
}
void ScriptRuntime::jshook_trace(JSTracer* trc, void* data)
{
ScriptRuntime* m = static_cast<ScriptRuntime*>(data);
if (m->m_rooter)
m->m_rooter->Trace(trc);
}
void ScriptRuntime::PrepareContextsForIncrementalGC()
{
for (std::list<JSContext*>::iterator itr = m_Contexts.begin(); itr != m_Contexts.end(); itr++)
{
JS::PrepareZoneForGC(js::GetCompartmentZone(js::GetContextCompartment(*itr)));
}
}
}

View File

@ -19,18 +19,12 @@
#define INCLUDED_SCRIPTRUNTIME
#include <sstream>
#include <boost/flyweight.hpp>
#include <boost/flyweight/key_value.hpp>
#include <boost/flyweight/no_locking.hpp>
#include <boost/flyweight/no_tracking.hpp>
#include "ScriptTypes.h"
#include "ScriptExtraHeaders.h"
#define STACK_CHUNK_SIZE 8192
class AutoGCRooter;
/**
* Abstraction around a SpiderMonkey JSRuntime.
* Each ScriptRuntime can be used to initialize several ScriptInterface
@ -44,7 +38,7 @@ class AutoGCRooter;
class ScriptRuntime
{
public:
ScriptRuntime(int runtimeSize, int heapGrowthBytesGCTrigger);
ScriptRuntime(shared_ptr<ScriptRuntime> parentRuntime, int runtimeSize, int heapGrowthBytesGCTrigger);
~ScriptRuntime();
/**
@ -56,106 +50,52 @@ public:
* in seconds (the delay should be a fraction of a second in most cases though).
* It will only start a new incremental GC or another GC slice if this time is exceeded. The user of this function is
* responsible for ensuring that GC can run with a small enough delay to get done with the work.
*/
*/
void MaybeIncrementalGC(double delay);
void ShrinkingGC();
void RegisterContext(JSContext* cx);
void UnRegisterContext(JSContext* cx);
/**
* Registers an object to be freed/finalized by the ScriptRuntime. Freeing is
* guaranteed to happen after the next minor GC has completed, but might also
* happen a bit later. This is only needed in very special situations
* and you should only use it if you know exactly why you need it!
* Specify a deleter for the shared_ptr to free the void pointer correctly
* (by casting to the right type before calling delete for example).
*/
void AddDeferredFinalizationObject(const std::shared_ptr<void>& obj);
JSRuntime* m_rt;
AutoGCRooter* m_rooter;
private:
void PrepareContextsForIncrementalGC();
void GCCallbackMember();
// Workaround for: https://bugzilla.mozilla.org/show_bug.cgi?id=890243
JSContext* m_dummyContext;
std::list<JSContext*> m_Contexts;
std::vector<std::shared_ptr<void> > m_FinalizationListObjectIdCache;
static bool m_Initialized;
int m_RuntimeSize;
int m_HeapGrowthBytesGCTrigger;
int m_LastGCBytes;
double m_LastGCCheck;
static void GCCallback(JSRuntime *rt, JSGCStatus status, void *data);
static void* jshook_script(JSContext* UNUSED(cx), JSAbstractFramePtr UNUSED(fp),
bool UNUSED(isConstructing), JSBool before,
JSBool* UNUSED(ok), void* closure);
bool UNUSED(isConstructing), bool before,
bool* UNUSED(ok), void* closure);
static void* jshook_function(JSContext* cx, JSAbstractFramePtr fp,
bool UNUSED(isConstructing), JSBool before,
JSBool* UNUSED(ok), void* closure);
static void jshook_trace(JSTracer* trc, void* data);
// To profile scripts usefully, we use a call hook that's called on every enter/exit,
// and need to find the function name. But most functions are anonymous so we make do
// with filename plus line number instead.
// Computing the names is fairly expensive, and we need to return an interned char*
// for the profiler to hold a copy of, so we use boost::flyweight to construct interned
// strings per call location.
// Identifies a location in a script
struct ScriptLocation
{
JSContext* cx;
JSScript* script;
jsbytecode* pc;
bool operator==(const ScriptLocation& b) const
{
return cx == b.cx && script == b.script && pc == b.pc;
}
friend std::size_t hash_value(const ScriptLocation& loc)
{
std::size_t seed = 0;
boost::hash_combine(seed, loc.cx);
boost::hash_combine(seed, loc.script);
boost::hash_combine(seed, loc.pc);
return seed;
}
};
// Computes and stores the name of a location in a script
struct ScriptLocationName
{
ScriptLocationName(const ScriptLocation& loc)
{
JSContext* cx = loc.cx;
JSScript* script = loc.script;
jsbytecode* pc = loc.pc;
std::string filename = JS_GetScriptFilename(cx, script);
size_t slash = filename.rfind('/');
if (slash != filename.npos)
filename = filename.substr(slash+1);
uint line = JS_PCToLineNumber(cx, script, pc);
std::stringstream ss;
ss << "(" << filename << ":" << line << ")";
name = ss.str();
}
std::string name;
};
// Flyweight types (with no_locking because the call hooks are only used in the
// main thread, and no_tracking because we mustn't delete values the profiler is
// using and it's not going to waste much memory)
typedef boost::flyweight<
std::string,
boost::flyweights::no_tracking,
boost::flyweights::no_locking
> StringFlyweight;
typedef boost::flyweight<
boost::flyweights::key_value<ScriptLocation, ScriptLocationName>,
boost::flyweights::no_tracking,
boost::flyweights::no_locking
> LocFlyweight;
bool UNUSED(isConstructing), bool before,
bool* UNUSED(ok), void* closure);
};
#endif // INCLUDED_SCRIPTRUNTIME
#endif // INCLUDED_SCRIPTRUNTIME

View File

@ -18,6 +18,9 @@
#ifndef INCLUDED_SCRIPTTYPES
#define INCLUDED_SCRIPTTYPES
#define JSGC_GENERATIONAL 1
#define JSGC_USE_EXACT_ROOTING 1
#ifdef _WIN32
# define XP_WIN
# ifndef WIN32
@ -27,29 +30,21 @@
// Guess whether the library was compiled with the release-mode or debug-mode ABI
// (for JS_DumpHeap etc)
#if defined(DEBUG) && !defined(WITH_SYSTEM_MOZJS24)
#if defined(DEBUG) && !defined(WITH_SYSTEM_MOZJS31)
# define MOZJS_DEBUG_ABI 1
#else
# define MOZJS_DEBUG_ABI 0
#endif
// Ignore some harmless warnings triggered by jsapi.h
// Not all of these warnings can be disabled in older versions of GCC.
// The version checking was taken from the manual here:
// http://gcc.gnu.org/onlinedocs/
// Choose the version and navigate to "Options to Request or Suppress Warnings"
// or for some flags "Options Controlling C++ Dialect".
#if GCC_VERSION >= 402
# if GCC_VERSION >= 406 // store user flags
# pragma GCC diagnostic push
# endif
// Ignore some harmless warnings
#if GCC_VERSION
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wunused-parameter"
# pragma GCC diagnostic ignored "-Wredundant-decls"
# pragma GCC diagnostic ignored "-Wundef" // Some versions of GCC will still print warnings (see http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53431).
# pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
# if GCC_VERSION >= 403
# pragma GCC diagnostic ignored "-Wignored-qualifiers"
# endif
# pragma GCC diagnostic ignored "-Wignored-qualifiers"
# pragma GCC diagnostic ignored "-Wextra"
#endif
#if CLANG_VERSION
# pragma clang diagnostic push
@ -65,46 +60,25 @@
# endif
#endif
#if MSC_VERSION
// warnings which are also disabled for the files that include this header
// For the transition from JSBool to bool
# pragma warning(disable:4800) // "forcing value to bool 'true' or 'false' (performance warning)
# pragma warning(disable:4805) // '==' : unsafe mix of type 'JSBool' and type 'bool' in operation
// warnings only disabled for the SpiderMonkey headers
# pragma warning(push)
# pragma warning(disable:4480) // "nonstandard extension used: specifying underlying type for enum"
# pragma warning(disable:4100) // "unreferenced formal parameter"
# pragma warning(disable:4265) // "class has virtual functions, but destructor is not virtual"
# pragma warning(disable:4251) // "class 'X' needs to have dll-interface to be used by clients of struct 'Y'"
# pragma warning(disable:4005) // "macro redefinition"
// reduce the warning level for the SpiderMonkey headers
# pragma warning(push, 1)
#endif
#include "jspubtd.h"
#include "jsapi.h"
// restore user flags and re-enable the warnings disabled a few lines above
#if MSC_VERSION
# pragma warning(pop)
#endif
#if CLANG_VERSION
# pragma clang diagnostic pop
#endif
#if GCC_VERSION >= 402
# pragma GCC diagnostic warning "-Wunused-parameter"
# pragma GCC diagnostic warning "-Wredundant-decls"
# pragma GCC diagnostic warning "-Wundef"
# pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
# if GCC_VERSION >= 403
# pragma GCC diagnostic warning "-Wignored-qualifiers"
# endif
# if GCC_VERSION >= 406
// restore user flags (and we don't have to manually restore the warning levels for GCC >= 4.6)
# pragma GCC diagnostic pop
# endif
#if GCC_VERSION
# pragma GCC diagnostic pop
#endif
#if MOZJS_MAJOR_VERSION != 24
#if MOZJS_MAJOR_VERSION != 31
#error Your compiler is trying to use an incorrect major version of the SpiderMonkey library.
#error The only version that works is the one in the libraries/spidermonkey/ directory,
#error and it will not work with a typical system-installed version.
@ -122,7 +96,5 @@
#endif
class ScriptInterface;
class CScriptVal;
class CScriptValRooted;
#endif // INCLUDED_SCRIPTTYPES

View File

@ -1,72 +0,0 @@
/* Copyright (C) 2010 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 "ScriptInterface.h"
#include "ScriptVal.h"
struct Unrooter
{
Unrooter(JSContext* cx) : cx(cx) { }
void operator()(jsval* p)
{
JSAutoRequest rq(cx);
JS_RemoveValueRoot(cx, p); delete p;
}
JSContext* cx;
};
CScriptValRooted::CScriptValRooted(JSContext* cx, jsval val)
{
JSAutoRequest rq(cx);
jsval* p = new jsval(val);
JS_AddNamedValueRoot(cx, p, "CScriptValRooted");
m_Val = boost::shared_ptr<jsval>(p, Unrooter(cx));
}
CScriptValRooted::CScriptValRooted(JSContext* cx, CScriptVal val)
{
JSAutoRequest rq(cx);
jsval* p = new jsval(val.get());
JS_AddNamedValueRoot(cx, p, "CScriptValRooted");
m_Val = boost::shared_ptr<jsval>(p, Unrooter(cx));
}
jsval CScriptValRooted::get() const
{
if (!m_Val)
return JSVAL_VOID;
return *m_Val;
}
jsval& CScriptValRooted::getRef() const
{
ENSURE(m_Val);
return *m_Val;
}
bool CScriptValRooted::undefined() const
{
return (!m_Val || JSVAL_IS_VOID(*m_Val));
}
bool CScriptValRooted::uninitialised() const
{
return !m_Val;
}

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2010 Wildfire Games.
/* Copyright (C) 2014 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -22,60 +22,65 @@
#include <boost/shared_ptr.hpp>
/**
* A trivial wrapper around a jsval. Used to avoid template overload ambiguities
* with jsval (which is just an integer), for any code that uses
* ScriptInterface::ToJSVal or ScriptInterface::FromJSVal
* A default constructible wrapper around JS::PersistentRootedValue
*
* It's a very common case that we need to store JS::Values on the heap as
* class members and only need them conditionally or want to initialize
* them after the constructor because we don't have the runtime available yet.
* Use it in these cases, but prefer to use JS::PersistentRootedValue directly
* if initializing it with a runtime/context in the constructor isn't a problem.
*/
class CScriptVal
template <typename T>
class DefPersistentRooted
{
public:
CScriptVal() : m_Val(JSVAL_VOID) { }
CScriptVal(jsval val) : m_Val(val) { }
DefPersistentRooted()
{
}
/**
* Returns the current value.
*/
const jsval& get() const { return m_Val; }
DefPersistentRooted(JSRuntime* rt)
{
m_Val.reset(new JS::PersistentRooted<T>(rt));
}
/**
* Returns whether the value is JSVAL_VOID.
*/
bool undefined() const { return JSVAL_IS_VOID(m_Val) ? true : false; }
DefPersistentRooted(JSRuntime* rt, JS::HandleValue val)
{
m_Val.reset(new JS::PersistentRooted<T>(rt, val));
}
DefPersistentRooted(JSContext* cx, JS::Handle<T> val)
{
m_Val.reset(new JS::PersistentRooted<T>(cx, val));
}
void clear()
{
m_Val = nullptr;
}
inline bool uninitialized()
{
return m_Val == nullptr;
}
inline JS::PersistentRooted<T>& get() const
{
ENSURE(m_Val);
return *m_Val;
}
inline void set(JSRuntime* rt, T val)
{
m_Val.reset(new JS::PersistentRooted<T>(rt, val));
}
inline void set(JSContext* cx, T val)
{
m_Val.reset(new JS::PersistentRooted<T>(cx, val));
}
private:
jsval m_Val;
};
class CScriptValRooted
{
public:
CScriptValRooted() { }
CScriptValRooted(JSContext* cx, jsval val);
CScriptValRooted(JSContext* cx, CScriptVal val);
/**
* Returns the current value (or JSVAL_VOID if uninitialised).
*/
jsval get() const;
/**
* Returns reference to the current value.
* Fails if the value is not yet initialised.
*/
jsval& getRef() const;
/**
* Returns whether the value is uninitialised or is JSVAL_VOID.
*/
bool undefined() const;
/**
* Returns whether the value is uninitialised.
*/
bool uninitialised() const;
private:
boost::shared_ptr<jsval> m_Val;
std::unique_ptr<JS::PersistentRooted<T> > m_Val;
};
#endif // INCLUDED_SCRIPTVAL

View File

@ -95,7 +95,7 @@ JSTrapStatus CheckForBreakRequestHandler_(JSContext* cx, JSScript* script, jsbyt
}
CMutex CallHookMutex;
static void* CallHook_(JSContext* cx, JSStackFrame* fp, JSBool before, JSBool* UNUSED(ok), void* closure)
static void* CallHook_(JSContext* cx, JSStackFrame* fp, bool before, bool* UNUSED(ok), void* closure)
{
CScopeLock lock(CallHookMutex);
CThreadDebugger* pThreadDebugger = (CThreadDebugger*) closure;
@ -673,7 +673,7 @@ void CThreadDebugger::SaveCallstack()
functionID = JS_NewStringCopyZ(m->m_pScriptInterface->GetContext(), "anonymous");
}
JSBool ret = JS_DefineElement(m->m_pScriptInterface->GetContext(), jsArray, counter, STRING_TO_JSVAL(functionID), NULL, NULL, 0);
bool ret = JS_DefineElement(m->m_pScriptInterface->GetContext(), jsArray, counter, STRING_TO_JSVAL(functionID), NULL, NULL, 0);
ENSURE(ret);
fp = JS_FrameIterator(m->m_pScriptInterface->GetContext(), &iter);
counter++;
@ -785,20 +785,20 @@ namespace CyclicRefWorkaround
struct Stringifier
{
static JSBool callback(const jschar* buf, uint32 len, void* data)
static bool callback(const jschar* buf, uint32 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 JS_TRUE;
return true;
}
std::stringstream stream;
};
JSBool replacer(JSContext* cx, uintN UNUSED(argc), jsval* vp)
bool replacer(JSContext* cx, uintN UNUSED(argc), jsval* vp)
{
jsval value = JS_ARGV(cx, vp)[1];
jsval key = JS_ARGV(cx, vp)[0];
@ -824,14 +824,14 @@ namespace CyclicRefWorkaround
JS_SET_RVAL(cx, vp, ret);
g_LastKey = key;
g_LastValue = value;
return JS_TRUE;
return true;
}
}
g_LastKey = key;
g_LastValue = value;
g_RecursionDetectedInPrevReplacer = false;
JS_SET_RVAL(cx, vp, JS_ARGV(cx, vp)[1]);
return JS_TRUE;
return true;
}
}

View File

@ -0,0 +1,64 @@
/* Copyright (C) 2014 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 "lib/self_test.h"
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/ScriptRuntime.h"
#include "scriptinterface/third_party/ObjectToIDMap.h"
class TestObjectToIDMap : public CxxTest::TestSuite
{
public:
void test_movinggc()
{
ScriptInterface script("Test", "Test", g_ScriptRuntime);
JSContext* cx = script.GetContext();
JSAutoRequest rq(cx);
JS::RootedObject obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
ObjectIdCache<u32> map(g_ScriptRuntime);
map.init();
TS_ASSERT(map.add(cx, obj, 1));
JSObject* plainObj = obj;
// The map should contain the object we've just added
TS_ASSERT(map.has(plainObj));
JS_GC(g_ScriptRuntime->m_rt);
// After a GC, the object should have been moved and plainObj should
// not be valid anymore and not be found in the map anymore.
// Obj should have an updated reference too, so it should still be found
// in the map.
//
// NOTE: It's observed behaviour that a full GC always moves an object.
// This might change in future SpiderMonkey versions. We only rely on
// that behaviour for this test.
//
// TODO: It might be a good idea to test the behaviour when only a minor
// GC runs, but there's no API for calling a minor GC yet.
JS_ASSERT(plainObj != obj);
TS_ASSERT(!map.has(plainObj));
TS_ASSERT(map.has(obj));
// Finding the ID associated with the object
u32 ret(0);
TS_ASSERT(map.find(obj, ret));
TS_ASSERT_EQUALS(ret, 1);
}
};

View File

@ -0,0 +1,130 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et tw=80:
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Providing a map-like structure with JSObject pointers (actually their hash) as keys
* with correct garbage collection handling (JSObjects can move in memory).
*
* The code in this class was copied from here and modified to work in our environment.
* * https://mxr.mozilla.org/mozilla-esr31/source/js/ipc/JavaScriptShared.h
* * https://mxr.mozilla.org/mozilla-esr31/source/js/ipc/JavaScriptShared.cpp
*
* When updating SpiderMonkey, you most likely have to reintegrate an updated version
* of the class(es) in this file. The best way is probably to get a diff between the
* original files and integrate that because this file is heavily modified from the
* original version.
*/
#ifndef INCLUDED_OBJECTTOIDMAP
#define INCLUDED_OBJECTTOIDMAP
#include "scriptinterface/ScriptRuntime.h"
#include "scriptinterface/ScriptTypes.h"
#include <stdint.h>
// Map JSObjects -> ids
template <typename T>
class ObjectIdCache
{
typedef js::PointerHasher<JSObject *, 3> Hasher;
typedef js::HashMap<JSObject *, T, Hasher, js::SystemAllocPolicy> ObjectIdTable;
public:
ObjectIdCache(shared_ptr<ScriptRuntime> rt)
: table_(nullptr), m_rt(rt)
{
JS_AddExtraGCRootsTracer(m_rt->m_rt, ObjectIdCache::Trace, this);
}
~ObjectIdCache()
{
if (table_) {
m_rt->AddDeferredFinalizationObject(std::shared_ptr<void>((void*)table_, DeleteObjectIDTable));
table_ = nullptr;
}
JS_RemoveExtraGCRootsTracer(m_rt->m_rt, ObjectIdCache::Trace, this);
}
bool init()
{
MOZ_ASSERT(!table_);
table_ = new ObjectIdTable(js::SystemAllocPolicy());
return table_ && table_->init(32);
}
void trace(JSTracer *trc)
{
for (typename ObjectIdTable::Range r(table_->all()); !r.empty(); r.popFront()) {
JSObject *obj = r.front().key();
JS_CallObjectTracer(trc, &obj, "ipc-id");
MOZ_ASSERT(obj == r.front().key());
}
}
bool add(JSContext *cx, JSObject *obj, T id)
{
if (!table_->put(obj, id))
return false;
JS_StoreObjectPostBarrierCallback(cx, keyMarkCallback, obj, table_);
return true;
}
bool find(JSObject *obj, T& ret)
{
typename ObjectIdTable::Ptr p = table_->lookup(obj);
if (!p)
return false;
ret = p->value();
return true;
}
void remove(JSObject *obj)
{
table_->remove(obj);
}
bool empty()
{
return table_->empty();
}
bool has(JSObject* obj)
{
return table_->has(obj);
}
private:
ObjectIdCache(const ObjectIdCache&) {};
ObjectIdCache& operator= (const ObjectIdCache& other) {};
static void keyMarkCallback(JSTracer *trc, JSObject *key, void *data)
{
ObjectIdTable* table = static_cast<ObjectIdTable*>(data);
JSObject *prior = key;
JS_CallObjectTracer(trc, &key, "ObjectIdCache::table_ key");
table->rekeyIfMoved(prior, key);
}
static void Trace(JSTracer *trc, void *data)
{
reinterpret_cast<ObjectIdCache*>(data)->trace(trc);
}
static void DeleteObjectIDTable(void* table)
{
delete (ObjectIdTable*)table;
}
shared_ptr<ScriptRuntime> m_rt;
ObjectIdTable *table_;
};
#endif // INCLUDED_OBJECTTOIDMAP

View File

@ -64,7 +64,8 @@ class CSimulation2Impl
public:
CSimulation2Impl(CUnitManager* unitManager, shared_ptr<ScriptRuntime> rt, CTerrain* terrain) :
m_SimContext(), m_ComponentManager(m_SimContext, rt),
m_EnableOOSLog(false), m_EnableSerializationTest(false)
m_EnableOOSLog(false), m_EnableSerializationTest(false),
m_MapSettings(rt->m_rt), m_InitAttributes(rt->m_rt)
{
m_SimContext.m_UnitManager = unitManager;
m_SimContext.m_Terrain = terrain;
@ -123,8 +124,8 @@ public:
float m_LastFrameOffset;
std::string m_StartupScript;
CScriptValRooted m_InitAttributes;
CScriptValRooted m_MapSettings;
JS::PersistentRootedValue m_InitAttributes;
JS::PersistentRootedValue m_MapSettings;
std::set<VfsPath> m_LoadedScripts;
@ -156,11 +157,15 @@ public:
JSContext* cxOld = oldScript.GetContext();
JSAutoRequest rqOld(cxOld);
std::vector<SimulationCommand> newCommands = commands;
for (size_t i = 0; i < newCommands.size(); ++i)
std::vector<SimulationCommand> newCommands;
newCommands.reserve(commands.size());
for (size_t i = 0; i < commands.size(); ++i)
{
JS::RootedValue tmpOldCommand(cxOld, newCommands[i].data.get()); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
newCommands[i].data = CScriptValRooted(newScript.GetContext(), newScript.CloneValueFromOtherContext(oldScript, tmpOldCommand));
JSContext* cxNew = newScript.GetContext();
JSAutoRequest rqNew(cxNew);
JS::RootedValue tmpCommand(cxNew, newScript.CloneValueFromOtherContext(oldScript, commands[i].data));
SimulationCommand cmd(commands[i].player, cxNew, tmpCommand);
newCommands.emplace_back(std::move(cmd));
}
return newCommands;
}
@ -334,6 +339,8 @@ void CSimulation2Impl::Update(int turnLength, const std::vector<SimulationComman
const bool serializationTestHash = true; // set true to save and compare hash of state
SerializationTestState primaryStateBefore;
ScriptInterface& scriptInterface = m_ComponentManager.GetScriptInterface();
if (m_EnableSerializationTest)
{
ENSURE(m_ComponentManager.SerializeState(primaryStateBefore.state));
@ -343,7 +350,6 @@ void CSimulation2Impl::Update(int turnLength, const std::vector<SimulationComman
ENSURE(m_ComponentManager.ComputeStateHash(primaryStateBefore.hash, false));
}
UpdateComponents(m_SimContext, turnLengthFixed, commands);
@ -353,23 +359,18 @@ void CSimulation2Impl::Update(int turnLength, const std::vector<SimulationComman
CTerrain secondaryTerrain;
CSimContext secondaryContext;
secondaryContext.m_Terrain = &secondaryTerrain;
CComponentManager secondaryComponentManager(secondaryContext, m_ComponentManager.GetScriptInterface().GetRuntime());
CComponentManager secondaryComponentManager(secondaryContext, scriptInterface.GetRuntime());
secondaryComponentManager.LoadComponentTypes();
ENSURE(LoadDefaultScripts(secondaryComponentManager, NULL));
ResetComponentState(secondaryComponentManager, false, false);
// Load the trigger scripts after we have loaded the simulation.
{
JSContext* cx1 = m_ComponentManager.GetScriptInterface().GetContext();
JSAutoRequest rq1(cx1);
// TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
JS::RootedValue tmpMapSettings(cx1, m_MapSettings.get());
JSContext* cx2 = secondaryComponentManager.GetScriptInterface().GetContext();
JSAutoRequest rq2(cx2);
JS::RootedValue mapSettingsCloned(cx2,
secondaryComponentManager.GetScriptInterface().CloneValueFromOtherContext(
m_ComponentManager.GetScriptInterface(), tmpMapSettings));
scriptInterface, m_MapSettings));
ENSURE(LoadTriggerScripts(secondaryComponentManager, mapSettingsCloned));
}
@ -377,33 +378,25 @@ void CSimulation2Impl::Update(int turnLength, const std::vector<SimulationComman
LDR_BeginRegistering();
CMapReader* mapReader = new CMapReader; // automatically deletes itself
// These braces limit the scope of rq and cx (mainly because we're working with different contexts here).
// TODO: Check after the upgrade to SpiderMonkey ESR31 if m_InitAtrributes can be made a PersistentRooted<T>
// and check if we even need a request in this case.
{
// TODO: this duplicates CWorld::RegisterInit and could probably be cleaned up a bit
JSContext* cx = m_ComponentManager.GetScriptInterface().GetContext();
JSAutoRequest rq(cx);
std::string mapType;
JS::RootedValue tmpInitAttributes(cx, m_InitAttributes.get()); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
m_ComponentManager.GetScriptInterface().GetProperty(tmpInitAttributes, "mapType", mapType);
if (mapType == "random")
{
// TODO: support random map scripts
debug_warn(L"Serialization test mode does not support random maps");
}
else
{
std::wstring mapFile;
m_ComponentManager.GetScriptInterface().GetProperty(tmpInitAttributes, "map", mapFile);
VfsPath mapfilename = VfsPath(mapFile).ChangeExtension(L".pmp");
mapReader->LoadMap(mapfilename, CScriptValRooted(), &secondaryTerrain, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, &secondaryContext, INVALID_PLAYER, true); // throws exception on failure
}
std::string mapType;
scriptInterface.GetProperty(m_InitAttributes, "mapType", mapType);
if (mapType == "random")
{
// TODO: support random map scripts
debug_warn(L"Serialization test mode does not support random maps");
}
else
{
std::wstring mapFile;
scriptInterface.GetProperty(m_InitAttributes, "map", mapFile);
VfsPath mapfilename = VfsPath(mapFile).ChangeExtension(L".pmp");
mapReader->LoadMap(mapfilename, scriptInterface.GetJSRuntime(), JS::UndefinedHandleValue,
&secondaryTerrain, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, &secondaryContext, INVALID_PLAYER, true); // throws exception on failure
}
LDR_EndRegistering();
ENSURE(LDR_NonprogressiveLoad() == INFO::OK);
@ -428,8 +421,7 @@ void CSimulation2Impl::Update(int turnLength, const std::vector<SimulationComman
ENSURE(m_ComponentManager.ComputeStateHash(primaryStateAfter.hash, false));
UpdateComponents(secondaryContext, turnLengthFixed,
CloneCommandsFromOtherContext(m_ComponentManager.GetScriptInterface(), secondaryComponentManager.GetScriptInterface(), commands));
CloneCommandsFromOtherContext(scriptInterface, secondaryComponentManager.GetScriptInterface(), commands));
SerializationTestState secondaryStateAfter;
ENSURE(secondaryComponentManager.SerializeState(secondaryStateAfter.state));
if (serializationTestHash)
@ -452,9 +444,17 @@ void CSimulation2Impl::Update(int turnLength, const std::vector<SimulationComman
// Run the GC occasionally
// No delay because a lot of garbage accumulates in one turn and in non-visual replays there are
// much more turns in the same time than in normal games.
// Every 500 turns we run a shrinking GC, which decommits unused memory and frees all JIT code.
// Based on testing, this seems to be a good compromise between memory usage and performance.
// Also check the comment about gcPreserveCode in the ScriptInterface code and this forum topic:
// http://www.wildfiregames.com/forum/index.php?showtopic=18466&p=300323
//
// (TODO: we ought to schedule this for a frame where we're not
// running the sim update, to spread the load)
m_ComponentManager.GetScriptInterface().GetRuntime()->MaybeIncrementalGC(0.0f);
if (m_TurnNumber % 500 == 0)
scriptInterface.GetRuntime()->ShrinkingGC();
else
scriptInterface.GetRuntime()->MaybeIncrementalGC(0.0f);
if (m_EnableOOSLog)
DumpState();
@ -657,7 +657,7 @@ void CSimulation2::PreInitGame()
GetScriptInterface().CallFunctionVoid(global, "PreInitGame");
}
void CSimulation2::InitGame(const CScriptVal& data)
void CSimulation2::InitGame(JS::HandleValue data)
{
JSContext* cx = GetScriptInterface().GetContext();
JSAutoRequest rq(cx);
@ -714,43 +714,34 @@ const std::string& CSimulation2::GetStartupScript()
return m->m_StartupScript;
}
void CSimulation2::SetInitAttributes(const CScriptValRooted& attribs)
void CSimulation2::SetInitAttributes(JS::HandleValue attribs)
{
m->m_InitAttributes = attribs;
}
CScriptValRooted CSimulation2::GetInitAttributes()
JS::Value CSimulation2::GetInitAttributes()
{
return m->m_InitAttributes;
return m->m_InitAttributes.get();
}
void CSimulation2::SetMapSettings(const std::string& settings)
{
JSContext* cx = m->m_ComponentManager.GetScriptInterface().GetContext();
JSAutoRequest rq(cx);
// TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
JS::RootedValue tmpMapSettings(cx);
m->m_ComponentManager.GetScriptInterface().ParseJSON(settings, &tmpMapSettings);
m->m_MapSettings = CScriptValRooted(cx, tmpMapSettings);
m->m_ComponentManager.GetScriptInterface().ParseJSON(settings, &m->m_MapSettings);
}
void CSimulation2::SetMapSettings(const CScriptValRooted& settings)
void CSimulation2::SetMapSettings(JS::HandleValue settings)
{
m->m_MapSettings = settings;
}
std::string CSimulation2::GetMapSettingsString()
{
JSContext* cx = m->m_ComponentManager.GetScriptInterface().GetContext();
JSAutoRequest rq(cx);
// TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
JS::RootedValue tmpMapSettings(cx, m->m_MapSettings.get());
return m->m_ComponentManager.GetScriptInterface().StringifyJSON(&tmpMapSettings);
return m->m_ComponentManager.GetScriptInterface().StringifyJSON(&m->m_MapSettings);
}
CScriptVal CSimulation2::GetMapSettings()
void CSimulation2::GetMapSettings(JS::MutableHandleValue ret)
{
return m->m_MapSettings.get();
ret.set(m->m_MapSettings);
}
void CSimulation2::LoadPlayerSettings(bool newPlayers)
@ -767,16 +758,15 @@ void CSimulation2::LoadMapSettings()
JSAutoRequest rq(cx);
JS::RootedValue global(cx, GetScriptInterface().GetGlobalObject());
JS::RootedValue tmpMapSettings(cx, m->m_MapSettings.get()); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
// Initialize here instead of in Update()
GetScriptInterface().CallFunctionVoid(global, "LoadMapSettings", tmpMapSettings);
GetScriptInterface().CallFunctionVoid(global, "LoadMapSettings", m->m_MapSettings);
if (!m->m_StartupScript.empty())
GetScriptInterface().LoadScript(L"map startup script", m->m_StartupScript);
// Load the trigger scripts after we have loaded the simulation and the map.
m->LoadTriggerScripts(m->m_ComponentManager, tmpMapSettings);
m->LoadTriggerScripts(m->m_ComponentManager, m->m_MapSettings);
}
int CSimulation2::ProgressiveLoad()
@ -895,7 +885,7 @@ std::string CSimulation2::GetAIData()
ScriptInterface& scriptInterface = GetScriptInterface();
JSContext* cx = scriptInterface.GetContext();
JSAutoRequest rq(cx);
std::vector<CScriptValRooted> aiData = ICmpAIManager::GetAIs(scriptInterface);
JS::RootedValue aiData(cx, ICmpAIManager::GetAIs(scriptInterface));
// Build single JSON string with array of AI data
JS::RootedValue ais(cx);

View File

@ -97,12 +97,12 @@ public:
* Set the attributes identifying the scenario/RMS used to initialise this
* simulation.
*/
void SetInitAttributes(const CScriptValRooted& settings);
void SetInitAttributes(JS::HandleValue settings);
/**
* Get the data passed to SetInitAttributes.
*/
CScriptValRooted GetInitAttributes();
JS::Value GetInitAttributes();
/**
* Set the initial map settings (as a UTF-8-encoded JSON string),
@ -114,7 +114,7 @@ public:
* Set the initial map settings, which will be used
* to set up the simulation state.
*/
void SetMapSettings(const CScriptValRooted& settings);
void SetMapSettings(JS::HandleValue settings);
/**
* Get the current map settings as a UTF-8 JSON string.
@ -124,7 +124,7 @@ public:
/**
* Get the current map settings.
*/
CScriptVal GetMapSettings();
void GetMapSettings(JS::MutableHandleValue ret);
/**
* RegMemFun incremental loader function.
@ -162,7 +162,7 @@ public:
* (This mustn't be used when e.g. loading saved games, only when starting new ones.)
* This calls the InitGame function defined in helpers/InitGame.js.
*/
void InitGame(const CScriptVal& data);
void InitGame(JS::HandleValue data);
void Update(int turnLength);
void Update(int turnLength, const std::vector<SimulationCommand>& commands);

View File

@ -79,17 +79,11 @@ private:
public:
CAIPlayer(CAIWorker& worker, const std::wstring& aiName, player_id_t player, uint8_t difficulty,
shared_ptr<ScriptInterface> scriptInterface) :
m_Worker(worker), m_AIName(aiName), m_Player(player), m_Difficulty(difficulty), m_ScriptInterface(scriptInterface)
m_Worker(worker), m_AIName(aiName), m_Player(player), m_Difficulty(difficulty),
m_ScriptInterface(scriptInterface), m_Obj(scriptInterface->GetJSRuntime())
{
}
~CAIPlayer()
{
// Clean up rooted objects before destroying their script context
m_Obj = CScriptValRooted();
m_Commands.clear();
}
bool Initialise()
{
// LoadScripts will only load each script once even though we call it for each player
@ -144,8 +138,6 @@ private:
m_ScriptInterface->GetProperty(metadata, "useShared", m_UseSharedComponent);
JS::RootedValue obj(cx);
// Set up the data to pass as the constructor argument
JS::RootedValue settings(cx);
m_ScriptInterface->Eval(L"({})", &settings);
@ -156,46 +148,32 @@ private:
JS::AutoValueVector argv(cx);
argv.append(settings.get());
m_ScriptInterface->CallConstructor(ctor, argv, &obj);
m_ScriptInterface->CallConstructor(ctor, argv, &m_Obj);
if (obj.isNull())
if (m_Obj.get().isNull())
{
LOGERROR("Failed to create AI player: %s: error calling constructor '%s'", path.string8(), constructor);
return false;
}
m_Obj = CScriptValRooted(cx, obj);
return true;
}
void Run(JS::HandleValue state, int playerID)
{
JSContext* cx = m_ScriptInterface->GetContext();
JSAutoRequest rq(cx);
JS::RootedValue tmpObj(cx, m_Obj.get()); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
m_Commands.clear();
m_ScriptInterface->CallFunctionVoid(tmpObj, "HandleMessage", state, playerID);
m_ScriptInterface->CallFunctionVoid(m_Obj, "HandleMessage", state, playerID);
}
// overloaded with a sharedAI part.
// javascript can handle both natively on the same function.
void Run(JS::HandleValue state, int playerID, CScriptValRooted SharedAI)
void Run(JS::HandleValue state, int playerID, JS::HandleValue SharedAI)
{
JSContext* cx = m_ScriptInterface->GetContext();
JSAutoRequest rq(cx);
JS::RootedValue tmpObj(cx, m_Obj.get()); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
m_Commands.clear();
m_ScriptInterface->CallFunctionVoid(tmpObj, "HandleMessage", state, playerID, SharedAI);
m_ScriptInterface->CallFunctionVoid(m_Obj, "HandleMessage", state, playerID, SharedAI);
}
void InitAI(JS::HandleValue state, CScriptValRooted SharedAI)
void InitAI(JS::HandleValue state, JS::HandleValue SharedAI)
{
JSContext* cx = m_ScriptInterface->GetContext();
JSAutoRequest rq(cx);
JS::RootedValue tmpObj(cx, m_Obj.get()); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
m_Commands.clear();
m_ScriptInterface->CallFunctionVoid(tmpObj, "Init", state, m_Player, SharedAI);
m_ScriptInterface->CallFunctionVoid(m_Obj, "Init", state, m_Player, SharedAI);
}
CAIWorker& m_Worker;
@ -204,8 +182,11 @@ private:
uint8_t m_Difficulty;
bool m_UseSharedComponent;
// Take care to keep this declaration before heap rooted members. Destructors of heap rooted
// members have to be called before the runtime destructor.
shared_ptr<ScriptInterface> m_ScriptInterface;
CScriptValRooted m_Obj;
JS::PersistentRootedValue m_Obj;
std::vector<shared_ptr<ScriptInterface::StructuredClone> > m_Commands;
};
@ -221,7 +202,13 @@ public:
m_TurnNum(0),
m_CommandsComputed(true),
m_HasLoadedEntityTemplates(false),
m_HasSharedComponent(false)
m_HasSharedComponent(false),
m_SerializablePrototypes(new ObjectIdCache<std::wstring>(g_ScriptRuntime)),
m_EntityTemplates(g_ScriptRuntime->m_rt),
m_TechTemplates(g_ScriptRuntime->m_rt),
m_SharedAIObj(g_ScriptRuntime->m_rt),
m_PassabilityMapVal(g_ScriptRuntime->m_rt),
m_TerritoryMapVal(g_ScriptRuntime->m_rt)
{
m_ScriptInterface->ReplaceNondeterministicRNG(m_RNG);
@ -229,7 +216,10 @@ public:
m_ScriptInterface->SetCallbackData(static_cast<void*> (this));
m_ScriptInterface->RegisterFunction<void, int, CScriptValRooted, CAIWorker::PostCommand>("PostCommand");
m_SerializablePrototypes->init();
JS_AddExtraGCRootsTracer(m_ScriptInterface->GetJSRuntime(), Trace, this);
m_ScriptInterface->RegisterFunction<void, int, JS::HandleValue, CAIWorker::PostCommand>("PostCommand");
m_ScriptInterface->RegisterFunction<void, std::wstring, CAIWorker::IncludeModule>("IncludeModule");
m_ScriptInterface->RegisterFunction<void, CAIWorker::DumpHeap>("DumpHeap");
m_ScriptInterface->RegisterFunction<void, CAIWorker::ForceGC>("ForceGC");
@ -239,13 +229,7 @@ public:
~CAIWorker()
{
// Clear rooted script values before destructing the script interface
m_EntityTemplates = CScriptValRooted();
m_PlayerMetadata.clear();
m_Players.clear();
m_GameState.reset();
m_PassabilityMapVal = CScriptValRooted();
m_TerritoryMapVal = CScriptValRooted();
JS_RemoveExtraGCRootsTracer(m_ScriptInterface->GetJSRuntime(), Trace, this);
}
bool LoadScripts(const std::wstring& moduleName)
@ -284,16 +268,10 @@ public:
self->LoadScripts(name);
}
static void PostCommand(ScriptInterface::CxPrivate* pCxPrivate, int playerid, CScriptValRooted cmd1)
static void PostCommand(ScriptInterface::CxPrivate* pCxPrivate, int playerid, JS::HandleValue cmd)
{
ENSURE(pCxPrivate->pCBData);
CAIWorker* self = static_cast<CAIWorker*> (pCxPrivate->pCBData);
JSContext* cx = pCxPrivate->pScriptInterface->GetContext();
JSAutoRequest rq(cx);
// TODO: Get Handle parameter directly with SpiderMonkey 31
JS::RootedValue cmd(cx, cmd1.get());
self->PostCommand(playerid, cmd);
}
@ -433,12 +411,9 @@ public:
JS::AutoValueVector argv(cx);
argv.append(settings);
// TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
JS::RootedValue tmpSharedAIObj(cx, m_SharedAIObj.get());
m_ScriptInterface->CallConstructor(ctor, argv, &tmpSharedAIObj);
m_SharedAIObj = CScriptValRooted(cx, tmpSharedAIObj);
m_ScriptInterface->CallConstructor(ctor, argv, &m_SharedAIObj);
if (tmpSharedAIObj.isNull())
if (m_SharedAIObj.get().isNull())
{
LOGERROR("Failed to create shared AI component: %s: error calling constructor '%s'", path.string8(), "SharedScript");
return false;
@ -472,22 +447,14 @@ public:
JS::RootedValue state(cx);
m_ScriptInterface->ReadStructuredClone(gameState, &state);
JS::RootedValue tmpVal(cx); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
ScriptInterface::ToJSVal(cx, &tmpVal, passabilityMap);
m_PassabilityMapVal = CScriptValRooted(cx, tmpVal.get());
ScriptInterface::ToJSVal(cx, &tmpVal, territoryMap);
m_TerritoryMapVal = CScriptValRooted(cx, tmpVal);
ScriptInterface::ToJSVal(cx, &m_PassabilityMapVal, passabilityMap);
ScriptInterface::ToJSVal(cx, &m_TerritoryMapVal, territoryMap);
if (m_HasSharedComponent)
{
m_ScriptInterface->SetProperty(state, "passabilityMap", m_PassabilityMapVal, true);
m_ScriptInterface->SetProperty(state, "territoryMap", m_TerritoryMapVal, true);
JS::RootedValue tmpSharedAIObj(cx, m_SharedAIObj.get()); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
m_ScriptInterface->CallFunctionVoid(tmpSharedAIObj, "init", state);
m_ScriptInterface->CallFunctionVoid(m_SharedAIObj, "init", state);
for (size_t i = 0; i < m_Players.size(); ++i)
{
@ -503,24 +470,18 @@ public:
ENSURE(m_CommandsComputed);
m_GameState = gameState;
JSContext* cx = m_ScriptInterface->GetContext();
JSAutoRequest rq(cx);
if (passabilityMap.m_DirtyID != m_PassabilityMap.m_DirtyID)
{
m_PassabilityMap = passabilityMap;
JS::RootedValue tmpVal(cx); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
ScriptInterface::ToJSVal(cx, &tmpVal, m_PassabilityMap);
m_PassabilityMapVal = CScriptValRooted(cx, tmpVal);
ScriptInterface::ToJSVal(cx, &m_PassabilityMapVal, m_PassabilityMap);
}
if (territoryMapDirty)
{
m_TerritoryMap = territoryMap;
JS::RootedValue tmpVal(cx);
ScriptInterface::ToJSVal(cx, &tmpVal, m_TerritoryMap);
m_TerritoryMapVal = CScriptValRooted(cx, tmpVal);
ScriptInterface::ToJSVal(cx, &m_TerritoryMapVal, m_TerritoryMap);
}
m_CommandsComputed = false;
@ -548,12 +509,9 @@ public:
}
}
void RegisterTechTemplates(const shared_ptr<ScriptInterface::StructuredClone>& techTemplates) {
JSContext* cx = m_ScriptInterface->GetContext();
JSAutoRequest rq(cx);
JS::RootedValue ret(cx); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
m_ScriptInterface->ReadStructuredClone(techTemplates, &ret);
m_TechTemplates = CScriptValRooted(cx, ret);
void RegisterTechTemplates(const shared_ptr<ScriptInterface::StructuredClone>& techTemplates)
{
m_ScriptInterface->ReadStructuredClone(techTemplates, &m_TechTemplates);
}
void LoadEntityTemplates(const std::vector<std::pair<std::string, const CParamNode*> >& templates)
@ -563,20 +521,18 @@ public:
m_HasLoadedEntityTemplates = true;
JS::RootedValue tmpEntityTemplates(cx); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
m_ScriptInterface->Eval("({})", &tmpEntityTemplates);
m_ScriptInterface->Eval("({})", &m_EntityTemplates);
JS::RootedValue val(cx);
for (size_t i = 0; i < templates.size(); ++i)
{
templates[i].second->ToJSVal(cx, false, &val);
m_ScriptInterface->SetProperty(tmpEntityTemplates, templates[i].first.c_str(), val, true);
m_ScriptInterface->SetProperty(m_EntityTemplates, templates[i].first.c_str(), val, true);
}
// Since the template data is shared between AI players, freeze it
// to stop any of them changing it and confusing the other players
m_EntityTemplates = CScriptValRooted(cx, tmpEntityTemplates);
m_ScriptInterface->FreezeObject(tmpEntityTemplates, true);
m_ScriptInterface->FreezeObject(m_EntityTemplates, true);
}
void Serialize(std::ostream& stream, bool isDebug)
@ -711,26 +667,24 @@ public:
// prototypes could be stored in their ScriptInterface
deserializer.SetSerializablePrototypes(m_DeserializablePrototypes);
JS::RootedValue tmpPlayerObj(cx, m_Players.back()->m_Obj.get()); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
bool hasCustomDeserialize = m_ScriptInterface->HasProperty(tmpPlayerObj, "Deserialize");
bool hasCustomDeserialize = m_ScriptInterface->HasProperty(m_Players.back()->m_Obj, "Deserialize");
if (hasCustomDeserialize)
{
JS::RootedValue scriptData(cx);
deserializer.ScriptVal("data", &scriptData);
if (m_Players[i]->m_UseSharedComponent)
{
if (!m_ScriptInterface->CallFunctionVoid(tmpPlayerObj, "Deserialize", scriptData, m_SharedAIObj))
if (!m_ScriptInterface->CallFunctionVoid(m_Players.back()->m_Obj, "Deserialize", scriptData, m_SharedAIObj))
LOGERROR("AI script Deserialize call failed");
}
else if (!m_ScriptInterface->CallFunctionVoid(tmpPlayerObj, "Deserialize", scriptData))
else if (!m_ScriptInterface->CallFunctionVoid(m_Players.back()->m_Obj, "Deserialize", scriptData))
{
LOGERROR("AI script deserialize() call failed");
}
}
else
{
deserializer.ScriptVal("data", &tmpPlayerObj);
m_Players.back()->m_Obj = CScriptValRooted(cx, tmpPlayerObj);
deserializer.ScriptVal("data", &m_Players.back()->m_Obj);
}
}
}
@ -740,25 +694,43 @@ public:
return m_Players.size();
}
void RegisterSerializablePrototype(std::wstring name, CScriptVal proto)
void RegisterSerializablePrototype(std::wstring name, JS::HandleValue proto)
{
// Require unique prototype and name (for reverse lookup)
// TODO: this is yucky - see comment in Deserialize()
JSObject* obj = JSVAL_TO_OBJECT(proto.get());
std::pair<std::map<JSObject*, std::wstring>::iterator, bool> ret1 = m_SerializablePrototypes.insert(std::make_pair(obj, name));
std::pair<std::map<std::wstring, JSObject*>::iterator, bool> ret2 = m_DeserializablePrototypes.insert(std::make_pair(name, obj));
if (!ret1.second || !ret2.second)
LOGERROR("RegisterSerializablePrototype called with same prototype multiple times: p=%p n='%s'", (void *)obj, utf8_from_wstring(name));
ENSURE(proto.isObject() && "A serializable prototype has to be an object!");
JS::RootedObject obj(m_ScriptInterface->GetContext(), &proto.toObject());
if (m_SerializablePrototypes->has(obj) || m_DeserializablePrototypes.find(name) != m_DeserializablePrototypes.end())
{
LOGERROR("RegisterSerializablePrototype called with same prototype multiple times: p=%p n='%s'", (void *)obj.get(), utf8_from_wstring(name));
return;
}
m_SerializablePrototypes->add(m_ScriptInterface->GetContext(), obj, name);
m_DeserializablePrototypes[name] = JS::Heap<JSObject*>(obj);
}
private:
static void Trace(JSTracer *trc, void *data)
{
reinterpret_cast<CAIWorker*>(data)->TraceMember(trc);
}
void TraceMember(JSTracer *trc)
{
for (auto& prototypes : m_DeserializablePrototypes)
JS_CallHeapObjectTracer(trc, &prototypes.second, "CAIWorker::m_DeserializablePrototypes");
for (auto& metadata : m_PlayerMetadata)
JS_CallHeapValueTracer(trc, &metadata.second, "CAIWorker::m_PlayerMetadata");
}
void LoadMetadata(const VfsPath& path, JS::MutableHandleValue out)
{
if (m_PlayerMetadata.find(path) == m_PlayerMetadata.end())
{
// Load and cache the AI player metadata
m_ScriptInterface->ReadJSONFile(path, out);
m_PlayerMetadata[path] = CScriptValRooted(m_ScriptInterface->GetContext(), out);
m_PlayerMetadata[path] = JS::Heap<JS::Value>(out);
return;
}
out.set(m_PlayerMetadata[path].get());
}
@ -803,34 +775,37 @@ private:
}
}
// Take care to keep this declaration before heap rooted members. Destructors of heap rooted
// members have to be called before the runtime destructor.
shared_ptr<ScriptRuntime> m_ScriptRuntime;
shared_ptr<ScriptInterface> m_ScriptInterface;
boost::rand48 m_RNG;
u32 m_TurnNum;
CScriptValRooted m_EntityTemplates;
JS::PersistentRootedValue m_EntityTemplates;
bool m_HasLoadedEntityTemplates;
CScriptValRooted m_TechTemplates;
JS::PersistentRootedValue m_TechTemplates;
std::map<VfsPath, CScriptValRooted> m_PlayerMetadata;
std::map<VfsPath, JS::Heap<JS::Value> > m_PlayerMetadata;
std::vector<shared_ptr<CAIPlayer> > m_Players; // use shared_ptr just to avoid copying
bool m_HasSharedComponent;
CScriptValRooted m_SharedAIObj;
JS::PersistentRootedValue m_SharedAIObj;
std::vector<SCommandSets> m_Commands;
std::set<std::wstring> m_LoadedModules;
shared_ptr<ScriptInterface::StructuredClone> m_GameState;
Grid<u16> m_PassabilityMap;
CScriptValRooted m_PassabilityMapVal;
JS::PersistentRootedValue m_PassabilityMapVal;
Grid<u8> m_TerritoryMap;
CScriptValRooted m_TerritoryMapVal;
JS::PersistentRootedValue m_TerritoryMapVal;
bool m_CommandsComputed;
std::map<JSObject*, std::wstring> m_SerializablePrototypes;
std::map<std::wstring, JSObject*> m_DeserializablePrototypes;
shared_ptr<ObjectIdCache<std::wstring> > m_SerializablePrototypes;
std::map<std::wstring, JS::Heap<JSObject*> > m_DeserializablePrototypes;
};
@ -936,7 +911,8 @@ public:
ENSURE(cmpTechTemplateManager);
// Get the game state from AIInterface
JS::RootedValue techTemplates(cx, cmpTechTemplateManager->GetAllTechs().get());
JS::RootedValue techTemplates(cx);
cmpTechTemplateManager->GetAllTechs(&techTemplates);
m_Worker.RegisterTechTemplates(scriptInterface.WriteStructuredClone(techTemplates));
m_Worker.TryLoadSharedComponent(true);
@ -953,7 +929,8 @@ public:
// Get the game state from AIInterface
// We flush events from the initialization so we get a clean state now.
JS::RootedValue state(cx, cmpAIInterface->GetFullRepresentation(true).get());
JS::RootedValue state(cx);
cmpAIInterface->GetFullRepresentation(&state, true);
// Get the passability data
Grid<u16> dummyGrid;
@ -996,9 +973,9 @@ public:
// Get the game state from AIInterface
JS::RootedValue state(cx);
if (m_JustDeserialized)
state.set(cmpAIInterface->GetFullRepresentation(false).get());
cmpAIInterface->GetFullRepresentation(&state, false);
else
state.set(cmpAIInterface->GetRepresentation().get());
cmpAIInterface->GetRepresentation(&state);
// Get the passability data
Grid<u16> dummyGrid;
@ -1045,7 +1022,7 @@ public:
for (size_t j = 0; j < commands[i].commands.size(); ++j)
{
scriptInterface.ReadStructuredClone(commands[i].commands[j], &clonedCommandVal);
cmpCommandQueue->PushLocalCommand(commands[i].player, CScriptVal(clonedCommandVal));
cmpCommandQueue->PushLocalCommand(commands[i].player, clonedCommandVal);
}
}
}

View File

@ -52,14 +52,12 @@ public:
{
JSContext* cx = GetSimContext().GetScriptInterface().GetContext();
JSAutoRequest rq(cx);
JS::RootedValue tmpRoot(cx); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
serialize.NumberU32_Unbounded("num commands", (u32)m_LocalQueue.size());
for (size_t i = 0; i < m_LocalQueue.size(); ++i)
{
tmpRoot.set(m_LocalQueue[i].data.get());
serialize.NumberI32_Unbounded("player", m_LocalQueue[i].player);
serialize.ScriptVal("data", &tmpRoot);
serialize.ScriptVal("data", &m_LocalQueue[i].data);
}
}
@ -76,25 +74,22 @@ public:
JS::RootedValue data(cx);
deserialize.NumberI32_Unbounded("player", player);
deserialize.ScriptVal("data", &data);
SimulationCommand c = { player, CScriptValRooted(cx, data) };
m_LocalQueue.push_back(c);
m_LocalQueue.emplace_back(SimulationCommand(player, cx, data));
}
}
virtual void PushLocalCommand(player_id_t player, CScriptVal cmd)
virtual void PushLocalCommand(player_id_t player, JS::HandleValue cmd)
{
JSContext* cx = GetSimContext().GetScriptInterface().GetContext();
SimulationCommand c = { player, CScriptValRooted(cx, cmd) };
m_LocalQueue.push_back(c);
m_LocalQueue.emplace_back(SimulationCommand(player, cx, cmd));
}
virtual void PostNetworkCommand(CScriptVal cmd1)
virtual void PostNetworkCommand(JS::HandleValue cmd1)
{
JSContext* cx = GetSimContext().GetScriptInterface().GetContext();
JSAutoRequest rq(cx);
// TODO: With ESR31 we should be able to take JS::HandleValue directly
// TODO: This is a workaround because we need to pass a MutableHandle to StringifyJSON.
JS::RootedValue cmd(cx, cmd1.get());
PROFILE2_EVENT("post net command");
@ -102,7 +97,7 @@ public:
// TODO: would be nicer to not use globals
if (g_Game && g_Game->GetTurnManager())
g_Game->GetTurnManager()->PostCommand(CScriptValRooted(cx, cmd));
g_Game->GetTurnManager()->PostCommand(cmd);
}
virtual void FlushTurn(const std::vector<SimulationCommand>& commands)

View File

@ -30,13 +30,13 @@ class CCmpAIInterfaceScripted : public ICmpAIInterface
public:
DEFAULT_SCRIPT_WRAPPER(AIInterfaceScripted)
virtual CScriptVal GetRepresentation()
virtual void GetRepresentation(JS::MutableHandleValue ret)
{
return m_Script.Call<CScriptVal>("GetRepresentation");
return m_Script.CallRef("GetRepresentation", ret);
}
virtual CScriptVal GetFullRepresentation(bool flushEvents = false)
virtual void GetFullRepresentation(JS::MutableHandleValue ret, bool flushEvents = false)
{
return m_Script.Call<CScriptVal>("GetFullRepresentation",flushEvents);
return m_Script.CallRef("GetFullRepresentation",flushEvents, ret);
}
};

View File

@ -27,12 +27,12 @@ public:
* Returns a script object that represents the current world state,
* to be passed to AI scripts.
*/
virtual CScriptVal GetRepresentation() = 0;
virtual void GetRepresentation(JS::MutableHandleValue ret) = 0;
/**
* Returns a script object that represents the current world state,
* to be passed to AI scripts. No caching for initialization
*/
virtual CScriptVal GetFullRepresentation(bool flushEvents) = 0;
virtual void GetFullRepresentation(JS::MutableHandleValue ret, bool flushEvents) = 0;
DECLARE_INTERFACE_TYPE(AIInterface)
};

View File

@ -39,8 +39,12 @@ struct GetAIsHelper
NONCOPYABLE(GetAIsHelper);
public:
GetAIsHelper(ScriptInterface& scriptInterface) :
m_ScriptInterface(scriptInterface)
m_ScriptInterface(scriptInterface),
m_AIs(scriptInterface.GetJSRuntime())
{
JSContext* cx = m_ScriptInterface.GetContext();
JSAutoRequest rq(cx);
m_AIs.set(JS_NewArrayObject(cx, 0));
}
void Run()
@ -66,18 +70,20 @@ public:
self->m_ScriptInterface.Eval("({})", &ai);
self->m_ScriptInterface.SetProperty(ai, "id", dirname, true);
self->m_ScriptInterface.SetProperty(ai, "data", data, true);
self->m_AIs.push_back(CScriptValRooted(cx, ai));
u32 length;
JS_GetArrayLength(cx, self->m_AIs, &length);
JS_SetElement(cx, self->m_AIs, length, ai);
return INFO::OK;
}
std::vector<CScriptValRooted> m_AIs;
JS::PersistentRootedObject m_AIs;
ScriptInterface& m_ScriptInterface;
};
std::vector<CScriptValRooted> ICmpAIManager::GetAIs(ScriptInterface& scriptInterface)
JS::Value ICmpAIManager::GetAIs(ScriptInterface& scriptInterface)
{
GetAIsHelper helper(scriptInterface);
helper.Run();
return helper.m_AIs;
return JS::ObjectValue(*helper.m_AIs);
}

View File

@ -51,7 +51,7 @@ public:
* Returns a vector of {"id":"value-for-AddPlayer", "name":"Human readable name"}
* objects, based on all the available AI scripts.
*/
static std::vector<CScriptValRooted> GetAIs(ScriptInterface& scriptInterface);
static JS::Value GetAIs(ScriptInterface& scriptInterface);
DECLARE_INTERFACE_TYPE(AIManager)
};

View File

@ -22,7 +22,7 @@
#include "simulation2/system/InterfaceScripted.h"
BEGIN_INTERFACE_WRAPPER(CommandQueue)
DEFINE_INTERFACE_METHOD_2("PushLocalCommand", void, ICmpCommandQueue, PushLocalCommand, player_id_t, CScriptVal)
DEFINE_INTERFACE_METHOD_1("PostNetworkCommand", void, ICmpCommandQueue, PostNetworkCommand, CScriptVal)
DEFINE_INTERFACE_METHOD_2("PushLocalCommand", void, ICmpCommandQueue, PushLocalCommand, player_id_t, JS::HandleValue)
DEFINE_INTERFACE_METHOD_1("PostNetworkCommand", void, ICmpCommandQueue, PostNetworkCommand, JS::HandleValue)
// Excluded: FlushTurn (doesn't make sense for scripts to call it)
END_INTERFACE_WRAPPER(CommandQueue)

View File

@ -40,12 +40,12 @@ public:
/**
* Pushes a new command onto the local queue. @p cmd does not need to be rooted.
*/
virtual void PushLocalCommand(player_id_t player, CScriptVal cmd) = 0;
virtual void PushLocalCommand(player_id_t player, JS::HandleValue cmd) = 0;
/**
* Send a command associated with the current player to the networking system.
*/
virtual void PostNetworkCommand(CScriptVal cmd) = 0;
virtual void PostNetworkCommand(JS::HandleValue cmd) = 0;
/**
* Calls the ProcessCommand(player, cmd) global script function for each command in the

View File

@ -24,17 +24,18 @@
#include "simulation2/system/SimContext.h"
#include "maths/FixedVector3D.h"
CScriptVal ICmpFootprint::GetShape_wrapper()
JS::Value ICmpFootprint::GetShape_wrapper()
{
EShape shape;
entity_pos_t size0, size1, height;
GetShape(shape, size0, size1, height);
JSContext* cx = GetSimContext().GetScriptInterface().GetContext();
JSAutoRequest rq(cx);
JSObject* obj = JS_NewObject(cx, NULL, NULL, NULL);
JS::RootedObject obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
if (!obj)
return JSVAL_VOID;
return JS::UndefinedValue();
if (shape == CIRCLE)
{
@ -44,9 +45,9 @@ CScriptVal ICmpFootprint::GetShape_wrapper()
ScriptInterface::ToJSVal<std::string>(cx, &ptype, "circle");
ScriptInterface::ToJSVal(cx, &pradius, size0);
ScriptInterface::ToJSVal(cx, &pheight, height);
JS_SetProperty(cx, obj, "type", ptype.address());
JS_SetProperty(cx, obj, "radius", pradius.address());
JS_SetProperty(cx, obj, "height", pheight.address());
JS_SetProperty(cx, obj, "type", ptype);
JS_SetProperty(cx, obj, "radius", pradius);
JS_SetProperty(cx, obj, "height", pheight);
}
else
{
@ -58,17 +59,17 @@ CScriptVal ICmpFootprint::GetShape_wrapper()
ScriptInterface::ToJSVal(cx, &pwidth, size0);
ScriptInterface::ToJSVal(cx, &pdepth, size1);
ScriptInterface::ToJSVal(cx, &pheight, height);
JS_SetProperty(cx, obj, "type", ptype.address());
JS_SetProperty(cx, obj, "width", pwidth.address());
JS_SetProperty(cx, obj, "depth", pdepth.address());
JS_SetProperty(cx, obj, "height", pheight.address());
JS_SetProperty(cx, obj, "type", ptype);
JS_SetProperty(cx, obj, "width", pwidth);
JS_SetProperty(cx, obj, "depth", pdepth);
JS_SetProperty(cx, obj, "height", pheight);
}
return OBJECT_TO_JSVAL(obj);
return JS::ObjectValue(*obj);
}
BEGIN_INTERFACE_WRAPPER(Footprint)
DEFINE_INTERFACE_METHOD_1("PickSpawnPoint", CFixedVector3D, ICmpFootprint, PickSpawnPoint, entity_id_t)
DEFINE_INTERFACE_METHOD_1("PickSpawnPointBothPass", CFixedVector3D, ICmpFootprint, PickSpawnPointBothPass, entity_id_t)
DEFINE_INTERFACE_METHOD_0("GetShape", CScriptVal, ICmpFootprint, GetShape_wrapper)
DEFINE_INTERFACE_METHOD_0("GetShape", JS::Value, ICmpFootprint, GetShape_wrapper)
END_INTERFACE_WRAPPER(Footprint)

View File

@ -54,7 +54,7 @@ public:
* Returns { "type": "circle", "radius": 5.0, "height": 1.0 }
* or { "type": "square", "width": 5.0, "depth": 5.0, "height": 1.0 }
*/
CScriptVal GetShape_wrapper();
JS::Value GetShape_wrapper();
/**
* Pick a sensible position to place a newly-spawned entity near this footprint,

View File

@ -30,9 +30,9 @@ class CCmpGuiInterfaceScripted : public ICmpGuiInterface
public:
DEFAULT_SCRIPT_WRAPPER(GuiInterfaceScripted)
virtual CScriptVal ScriptCall(int player, const std::wstring& cmd, CScriptVal data)
virtual void ScriptCall(int player, const std::wstring& cmd, JS::HandleValue data, JS::MutableHandleValue ret)
{
return m_Script.Call<CScriptVal>("ScriptCall", player, cmd, data);
m_Script.CallRef("ScriptCall", player, cmd, data, ret);
}
};

View File

@ -28,7 +28,7 @@ public:
/**
* Generic call function, for use by GUI scripts to talk to the GuiInterface script.
*/
virtual CScriptVal ScriptCall(int player, const std::wstring& cmd, CScriptVal data) = 0;
virtual void ScriptCall(int player, const std::wstring& cmd, JS::HandleValue data, JS::MutableHandleValue ret) = 0;
// TODO: some of the earlier functions should just use ScriptCall.
DECLARE_INTERFACE_TYPE(GuiInterface)

View File

@ -30,9 +30,9 @@ class CCmpTechnologyTemplateManagerScripted : public ICmpTechnologyTemplateManag
public:
DEFAULT_SCRIPT_WRAPPER(TechnologyTemplateManagerScripted)
virtual CScriptVal GetAllTechs()
virtual void GetAllTechs(JS::MutableHandleValue ret)
{
return m_Script.Call<CScriptVal>("GetAllTechs");
return m_Script.CallRef("GetAllTechs", ret);
}
};

View File

@ -30,7 +30,7 @@
class ICmpTechnologyTemplateManager : public IComponent
{
public:
virtual CScriptVal GetAllTechs() = 0;
virtual void GetAllTechs(JS::MutableHandleValue ret) = 0;
DECLARE_INTERFACE_TYPE(TechnologyTemplateManager)
};

View File

@ -35,6 +35,8 @@ public:
void test_basic()
{
ComponentTestHelper test(g_ScriptRuntime);
JSContext* cx = test.GetScriptInterface().GetContext();
JSAutoRequest rq(cx);
std::vector<SimulationCommand> empty;
@ -42,12 +44,12 @@ public:
TS_ASSERT(test.GetScriptInterface().Eval("var cmds = []; function ProcessCommand(player, cmd) { cmds.push([player, cmd]); }"));
CScriptVal cmd;
JS::RootedValue cmd(cx);
TS_ASSERT(test.GetScriptInterface().Eval("([1,2,3])", cmd));
TS_ASSERT(test.GetScriptInterface().Eval("([1,2,3])", &cmd));
cmp->PushLocalCommand(1, cmd);
TS_ASSERT(test.GetScriptInterface().Eval("({x:4})", cmd));
TS_ASSERT(test.GetScriptInterface().Eval("({x:4})", &cmd));
cmp->PushLocalCommand(-1, cmd);
test.Roundtrip();
@ -55,7 +57,7 @@ public:
// Process the first two commands
cmp->FlushTurn(empty);
TS_ASSERT(test.GetScriptInterface().Eval("({y:5})", cmd));
TS_ASSERT(test.GetScriptInterface().Eval("({y:5})", &cmd));
cmp->PushLocalCommand(10, cmd);
// Process the next command

View File

@ -66,7 +66,9 @@ public:
CMapReader* mapReader = new CMapReader(); // it'll call "delete this" itself
LDR_BeginRegistering();
mapReader->LoadMap(L"maps/skirmishes/Median Oasis (2).pmp", CScriptValRooted(), &terrain, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
mapReader->LoadMap(L"maps/skirmishes/Median Oasis (2).pmp",
sim2.GetScriptInterface().GetJSRuntime(), JS::UndefinedHandleValue,
&terrain, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
&sim2, &sim2.GetSimContext(), -1, false);
LDR_EndRegistering();
TS_ASSERT_OK(LDR_NonprogressiveLoad());

View File

@ -18,7 +18,7 @@
#ifndef INCLUDED_SIMULATIONCOMMAND
#define INCLUDED_SIMULATIONCOMMAND
#include "scriptinterface/ScriptVal.h"
#include "scriptinterface/ScriptInterface.h"
#include "simulation2/helpers/Player.h"
/**
@ -26,8 +26,27 @@
*/
struct SimulationCommand
{
SimulationCommand(player_id_t player, JSContext* cx, JS::HandleValue val)
: player(player), data(cx, val)
{
}
SimulationCommand(SimulationCommand&& cmd)
: player(cmd.player), data(cmd.data)
{
}
// std::vector::insert requires the move assignment operator at compilation time,
// but apparently never uses it (it uses the move constructor).
SimulationCommand& operator=(SimulationCommand&& other)
{
this->player = other.player;
this->data.set(other.data);
return *this;
}
player_id_t player;
CScriptValRooted data;
JS::PersistentRootedValue data;
};
#endif // INCLUDED_SIMULATIONCOMMAND

View File

@ -72,7 +72,10 @@ template<> void ScriptInterface::ToJSVal<CParamNode>(JSContext* cx, JS::MutableH
// Prevent modifications to the object, so that it's safe to share between
// components and to reconstruct on deserialization
if (ret.isObject())
JS_DeepFreezeObject(cx, &ret.toObject());
{
JS::RootedObject obj(cx, &ret.toObject());
JS_DeepFreezeObject(cx, obj);
}
}
template<> void ScriptInterface::ToJSVal<const CParamNode*>(JSContext* cx, JS::MutableHandleValue ret, const CParamNode* const& val)
@ -95,13 +98,13 @@ template<> bool ScriptInterface::FromJSVal<CColor>(JSContext* cx, JS::HandleValu
JS::RootedValue g(cx);
JS::RootedValue b(cx);
JS::RootedValue a(cx);
if (!JS_GetProperty(cx, obj, "r", r.address()) || !FromJSVal(cx, r, out.r))
if (!JS_GetProperty(cx, obj, "r", &r) || !FromJSVal(cx, r, out.r))
FAIL("Failed to get property CColor.r");
if (!JS_GetProperty(cx, obj, "g", g.address()) || !FromJSVal(cx, g, out.g))
if (!JS_GetProperty(cx, obj, "g", &g) || !FromJSVal(cx, g, out.g))
FAIL("Failed to get property CColor.g");
if (!JS_GetProperty(cx, obj, "b", b.address()) || !FromJSVal(cx, b, out.b))
if (!JS_GetProperty(cx, obj, "b", &b) || !FromJSVal(cx, b, out.b))
FAIL("Failed to get property CColor.b");
if (!JS_GetProperty(cx, obj, "a", a.address()) || !FromJSVal(cx, a, out.a))
if (!JS_GetProperty(cx, obj, "a", &a) || !FromJSVal(cx, a, out.a))
FAIL("Failed to get property CColor.a");
return true;
@ -110,7 +113,7 @@ template<> bool ScriptInterface::FromJSVal<CColor>(JSContext* cx, JS::HandleValu
template<> void ScriptInterface::ToJSVal<CColor>(JSContext* cx, JS::MutableHandleValue ret, CColor const& val)
{
JSAutoRequest rq(cx);
JS::RootedObject obj(cx, JS_NewObject(cx, NULL, NULL, NULL));
JS::RootedObject obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
if (!obj)
{
ret.setUndefined();
@ -126,10 +129,10 @@ template<> void ScriptInterface::ToJSVal<CColor>(JSContext* cx, JS::MutableHandl
ToJSVal(cx, &b, val.b);
ToJSVal(cx, &a, val.a);
JS_SetProperty(cx, obj, "r", r.address());
JS_SetProperty(cx, obj, "g", g.address());
JS_SetProperty(cx, obj, "b", b.address());
JS_SetProperty(cx, obj, "a", a.address());
JS_SetProperty(cx, obj, "r", r);
JS_SetProperty(cx, obj, "g", g);
JS_SetProperty(cx, obj, "b", b);
JS_SetProperty(cx, obj, "a", a);
ret.setObject(*obj);
}
@ -160,13 +163,13 @@ template<> bool ScriptInterface::FromJSVal<CFixedVector3D>(JSContext* cx, JS::Ha
JS::RootedObject obj(cx, &v.toObject());
JS::RootedValue p(cx);
if (!JS_GetProperty(cx, obj, "x", p.address())) return false; // TODO: report type errors
if (!JS_GetProperty(cx, obj, "x", &p)) return false; // TODO: report type errors
if (!FromJSVal(cx, p, out.X)) return false;
if (!JS_GetProperty(cx, obj, "y", p.address())) return false;
if (!JS_GetProperty(cx, obj, "y", &p)) return false;
if (!FromJSVal(cx, p, out.Y)) return false;
if (!JS_GetProperty(cx, obj, "z", p.address())) return false;
if (!JS_GetProperty(cx, obj, "z", &p)) return false;
if (!FromJSVal(cx, p, out.Z)) return false;
return true;
@ -178,9 +181,8 @@ template<> void ScriptInterface::ToJSVal<CFixedVector3D>(JSContext* cx, JS::Muta
// apply the Vector3D prototype to the return value;
ScriptInterface::CxPrivate* pCxPrivate = ScriptInterface::GetScriptInterfaceAndCBData(cx);
JS::RootedObject obj(cx, JS_NewObject(cx, NULL,
&pCxPrivate->pScriptInterface->GetCachedValue(ScriptInterface::CACHE_VECTOR3DPROTO).get().toObject(),
NULL));
JS::RootedObject proto(cx, &pCxPrivate->pScriptInterface->GetCachedValue(ScriptInterface::CACHE_VECTOR3DPROTO).toObject());
JS::RootedObject obj(cx, JS_NewObject(cx, nullptr, proto, JS::NullPtr()));
if (!obj)
{
@ -195,9 +197,9 @@ template<> void ScriptInterface::ToJSVal<CFixedVector3D>(JSContext* cx, JS::Muta
ToJSVal(cx, &y, val.Y);
ToJSVal(cx, &z, val.Z);
JS_SetProperty(cx, obj, "x", x.address());
JS_SetProperty(cx, obj, "y", y.address());
JS_SetProperty(cx, obj, "z", z.address());
JS_SetProperty(cx, obj, "x", x);
JS_SetProperty(cx, obj, "y", y);
JS_SetProperty(cx, obj, "z", z);
ret.setObject(*obj);
}
@ -211,10 +213,10 @@ template<> bool ScriptInterface::FromJSVal<CFixedVector2D>(JSContext* cx, JS::Ha
JS::RootedValue p(cx);
if (!JS_GetProperty(cx, obj, "x", p.address())) return false; // TODO: report type errors
if (!JS_GetProperty(cx, obj, "x", &p)) return false; // TODO: report type errors
if (!FromJSVal(cx, p, out.X)) return false;
if (!JS_GetProperty(cx, obj, "y", p.address())) return false;
if (!JS_GetProperty(cx, obj, "y", &p)) return false;
if (!FromJSVal(cx, p, out.Y)) return false;
return true;
@ -226,9 +228,8 @@ template<> void ScriptInterface::ToJSVal<CFixedVector2D>(JSContext* cx, JS::Muta
// apply the Vector2D prototype to the return value
ScriptInterface::CxPrivate* pCxPrivate = ScriptInterface::GetScriptInterfaceAndCBData(cx);
JS::RootedObject obj(cx, JS_NewObject(cx, NULL,
&pCxPrivate->pScriptInterface->GetCachedValue(ScriptInterface::CACHE_VECTOR2DPROTO).get().toObject(),
NULL));
JS::RootedObject proto(cx, &pCxPrivate->pScriptInterface->GetCachedValue(ScriptInterface::CACHE_VECTOR2DPROTO).toObject());
JS::RootedObject obj(cx, JS_NewObject(cx, nullptr, proto, JS::NullPtr()));
if (!obj)
{
ret.setUndefined();
@ -240,8 +241,8 @@ template<> void ScriptInterface::ToJSVal<CFixedVector2D>(JSContext* cx, JS::Muta
ToJSVal(cx, &x, val.X);
ToJSVal(cx, &y, val.Y);
JS_SetProperty(cx, obj, "x", x.address());
JS_SetProperty(cx, obj, "y", y.address());
JS_SetProperty(cx, obj, "x", x);
JS_SetProperty(cx, obj, "y", y);
ret.setObject(*obj);
}
@ -260,10 +261,10 @@ template<> void ScriptInterface::ToJSVal<Grid<u8> >(JSContext* cx, JS::MutableHa
ScriptInterface::ToJSVal(cx, &w, val.m_W);
ScriptInterface::ToJSVal(cx, &h, val.m_H);
JS::RootedObject obj(cx, JS_NewObject(cx, NULL, NULL, NULL));
JS_SetProperty(cx, obj, "width", w.address());
JS_SetProperty(cx, obj, "height", h.address());
JS_SetProperty(cx, obj, "data", data.address());
JS::RootedObject obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
JS_SetProperty(cx, obj, "width", w);
JS_SetProperty(cx, obj, "height", h);
JS_SetProperty(cx, obj, "data", data);
ret.setObject(*obj);
}
@ -282,10 +283,10 @@ template<> void ScriptInterface::ToJSVal<Grid<u16> >(JSContext* cx, JS::MutableH
ScriptInterface::ToJSVal(cx, &w, val.m_W);
ScriptInterface::ToJSVal(cx, &h, val.m_H);
JS::RootedObject obj(cx, JS_NewObject(cx, NULL, NULL, NULL));
JS_SetProperty(cx, obj, "width", w.address());
JS_SetProperty(cx, obj, "height", h.address());
JS_SetProperty(cx, obj, "data", data.address());
JS::RootedObject obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
JS_SetProperty(cx, obj, "width", w);
JS_SetProperty(cx, obj, "height", h);
JS_SetProperty(cx, obj, "data", data);
ret.setObject(*obj);
}

View File

@ -24,7 +24,7 @@
#define TOJSVAL_SETUP() \
JSContext* cx = scriptInterface.GetContext(); \
JSAutoRequest rq(cx); \
JS::RootedObject obj(cx, JS_NewObject(cx, NULL, NULL, NULL)); \
JS::RootedObject obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr())); \
if (!obj) \
return JS::UndefinedValue();
@ -32,7 +32,7 @@
do { \
JS::RootedValue prop(cx);\
ScriptInterface::ToJSVal(cx, &prop, this->name); \
if (! JS_SetProperty(cx, obj, #name, prop.address())) \
if (! JS_SetProperty(cx, obj, #name, prop)) \
return JS::UndefinedValue(); \
} while (0);
@ -47,7 +47,7 @@
#define GET_MSG_PROPERTY(type, name) \
type name; \
{ \
if (! JS_GetProperty(cx, obj, #name, prop.address())) \
if (! JS_GetProperty(cx, obj, #name, &prop)) \
return NULL; \
if (! ScriptInterface::FromJSVal(cx, prop, name)) \
return NULL; \
@ -55,10 +55,10 @@
JS::Value CMessage::ToJSValCached(ScriptInterface& scriptInterface) const
{
if (m_Cached.uninitialised())
m_Cached = CScriptValRooted(scriptInterface.GetContext(), ToJSVal(scriptInterface));
if (!m_Cached)
m_Cached.reset(new JS::PersistentRootedValue(scriptInterface.GetJSRuntime(), ToJSVal(scriptInterface)));
return m_Cached.get();
return m_Cached->get();
}
////////////////////////////////

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2010 Wildfire Games.
/* Copyright (C) 2015 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -22,44 +22,35 @@
#include "simulation2/serialization/ISerializer.h"
#include "simulation2/serialization/IDeserializer.h"
CComponentTypeScript::CComponentTypeScript(ScriptInterface& scriptInterface, jsval instance) :
m_ScriptInterface(scriptInterface), m_Instance(CScriptValRooted(scriptInterface.GetContext(), instance))
CComponentTypeScript::CComponentTypeScript(ScriptInterface& scriptInterface, JS::HandleValue instance) :
m_ScriptInterface(scriptInterface), m_Instance(scriptInterface.GetJSRuntime(), instance)
{
// Cache the property detection for efficiency
JSContext* cx = m_ScriptInterface.GetContext();
JSAutoRequest rq(cx);
JS::RootedValue tmpInstance(cx, m_Instance.get()); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
m_HasCustomSerialize = m_ScriptInterface.HasProperty(tmpInstance, "Serialize");
m_HasCustomDeserialize = m_ScriptInterface.HasProperty(tmpInstance, "Deserialize");
m_HasCustomSerialize = m_ScriptInterface.HasProperty(m_Instance, "Serialize");
m_HasCustomDeserialize = m_ScriptInterface.HasProperty(m_Instance, "Deserialize");
m_HasNullSerialize = false;
if (m_HasCustomSerialize)
{
JS::RootedValue val(cx);
if (m_ScriptInterface.GetProperty(tmpInstance, "Serialize", &val) && val.isNull())
if (m_ScriptInterface.GetProperty(m_Instance, "Serialize", &val) && val.isNull())
m_HasNullSerialize = true;
}
}
void CComponentTypeScript::Init(const CParamNode& paramNode, entity_id_t ent)
{
JSContext* cx = m_ScriptInterface.GetContext();
JSAutoRequest rq(cx);
JS::RootedValue tmpInstance(cx, m_Instance.get()); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
m_ScriptInterface.SetProperty(tmpInstance, "entity", (int)ent, true, false);
m_ScriptInterface.SetProperty(tmpInstance, "template", paramNode, true, false);
m_ScriptInterface.CallFunctionVoid(tmpInstance, "Init");
m_ScriptInterface.SetProperty(m_Instance, "entity", (int)ent, true, false);
m_ScriptInterface.SetProperty(m_Instance, "template", paramNode, true, false);
m_ScriptInterface.CallFunctionVoid(m_Instance, "Init");
}
void CComponentTypeScript::Deinit()
{
JSContext* cx = m_ScriptInterface.GetContext();
JSAutoRequest rq(cx);
JS::RootedValue tmpInstance(cx, m_Instance.get()); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
m_ScriptInterface.CallFunctionVoid(tmpInstance, "Deinit");
{
m_ScriptInterface.CallFunctionVoid(m_Instance, "Deinit");
}
void CComponentTypeScript::HandleMessage(const CMessage& msg, bool global)
@ -71,8 +62,7 @@ void CComponentTypeScript::HandleMessage(const CMessage& msg, bool global)
JS::RootedValue msgVal(cx, msg.ToJSValCached(m_ScriptInterface));
JS::RootedValue tmpInstance(cx, m_Instance.get()); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
if (!m_ScriptInterface.CallFunctionVoid(tmpInstance, name, msgVal))
if (!m_ScriptInterface.CallFunctionVoid(m_Instance, name, msgVal))
LOGERROR("Script message handler %s failed", name);
}
@ -84,20 +74,19 @@ void CComponentTypeScript::Serialize(ISerializer& serialize)
JSContext* cx = m_ScriptInterface.GetContext();
JSAutoRequest rq(cx);
JS::RootedValue tmpInstance(cx, m_Instance.get()); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
// Support a custom "Serialize" function, which returns a new object that will be
// serialized instead of the component itself
if (m_HasCustomSerialize)
{
JS::RootedValue val(cx);
if (!m_ScriptInterface.CallFunction(tmpInstance, "Serialize", &val))
if (!m_ScriptInterface.CallFunction(m_Instance, "Serialize", &val))
LOGERROR("Script Serialize call failed");
serialize.ScriptVal("object", &val);
}
else
{
serialize.ScriptVal("object", &tmpInstance);
serialize.ScriptVal("object", &m_Instance);
}
}
@ -106,7 +95,6 @@ void CComponentTypeScript::Deserialize(const CParamNode& paramNode, IDeserialize
JSContext* cx = m_ScriptInterface.GetContext();
JSAutoRequest rq(cx);
JS::RootedValue tmpInstance(cx, m_Instance.get()); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
// Support a custom "Deserialize" function, to which we pass the deserialized data
// instead of automatically adding the deserialized properties onto the object
if (m_HasCustomDeserialize)
@ -117,7 +105,7 @@ void CComponentTypeScript::Deserialize(const CParamNode& paramNode, IDeserialize
if (!m_HasNullSerialize)
deserialize.ScriptVal("object", &val);
if (!m_ScriptInterface.CallFunctionVoid(tmpInstance, "Deserialize", val))
if (!m_ScriptInterface.CallFunctionVoid(m_Instance, "Deserialize", val))
LOGERROR("Script Deserialize call failed");
}
else
@ -126,10 +114,10 @@ void CComponentTypeScript::Deserialize(const CParamNode& paramNode, IDeserialize
{
// Use ScriptObjectAppend so we don't lose the carefully-constructed
// prototype/parent of this object
deserialize.ScriptObjectAppend("object", tmpInstance);
deserialize.ScriptObjectAppend("object", m_Instance);
}
}
m_ScriptInterface.SetProperty(tmpInstance, "entity", (int)ent, true, false);
m_ScriptInterface.SetProperty(tmpInstance, "template", paramNode, true, false);
m_ScriptInterface.SetProperty(m_Instance, "entity", (int)ent, true, false);
m_ScriptInterface.SetProperty(m_Instance, "template", paramNode, true, false);
}

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2010 Wildfire Games.
/* Copyright (C) 2014 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -34,9 +34,9 @@ class IDeserializer;
class CComponentTypeScript
{
public:
CComponentTypeScript(ScriptInterface& scriptInterface, jsval instance);
CComponentTypeScript(ScriptInterface& scriptInterface, JS::HandleValue instance);
jsval GetInstance() const { return m_Instance.get(); }
JS::Value GetInstance() const { return m_Instance.get(); }
void Init(const CParamNode& paramNode, entity_id_t ent);
void Deinit();
@ -53,27 +53,27 @@ public:
// template<typename T0> void CallVoid(const char* funcname, const T0& a0);
// ...
// TODO: Check if these temporary roots can be removed after SpiderMonkey 31 upgrade
// CallRef is mainly used for returning script values with correct stack rooting.
#define OVERLOADS(z, i, data) \
template<typename R BOOST_PP_ENUM_TRAILING_PARAMS(i, typename T)> \
R Call(const char* funcname BOOST_PP_ENUM_TRAILING_BINARY_PARAMS(i, const T, &a)) \
{ \
JSContext* cx = m_ScriptInterface.GetContext(); \
JSAutoRequest rq(cx); \
JS::RootedValue tmpInstance(cx, m_Instance.get()); \
R ret; \
if (m_ScriptInterface.CallFunction(tmpInstance, funcname BOOST_PP_ENUM_TRAILING_PARAMS(i, a), ret)) \
if (m_ScriptInterface.CallFunction(m_Instance, funcname BOOST_PP_ENUM_TRAILING_PARAMS(i, a), ret)) \
return ret; \
LOGERROR("Error calling component script function %s", funcname); \
return R(); \
} \
template<typename R BOOST_PP_ENUM_TRAILING_PARAMS(i, typename T)> \
void CallRef(const char* funcname BOOST_PP_ENUM_TRAILING_BINARY_PARAMS(i, const T, &a), R ret) \
{ \
if (!m_ScriptInterface.CallFunction(m_Instance, funcname BOOST_PP_ENUM_TRAILING_PARAMS(i, a), ret)) \
LOGERROR("Error calling component script function %s", funcname); \
} \
BOOST_PP_IF(i, template<, ) BOOST_PP_ENUM_PARAMS(i, typename T) BOOST_PP_IF(i, >, ) \
void CallVoid(const char* funcname BOOST_PP_ENUM_TRAILING_BINARY_PARAMS(i, const T, &a)) \
{ \
JSContext* cx = m_ScriptInterface.GetContext(); \
JSAutoRequest rq(cx); \
JS::RootedValue tmpInstance(cx, m_Instance.get()); \
if (m_ScriptInterface.CallFunctionVoid(tmpInstance, funcname BOOST_PP_ENUM_TRAILING_PARAMS(i, a))) \
if (m_ScriptInterface.CallFunctionVoid(m_Instance, funcname BOOST_PP_ENUM_TRAILING_PARAMS(i, a))) \
return; \
LOGERROR("Error calling component script function %s", funcname); \
}
@ -82,7 +82,7 @@ BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
private:
ScriptInterface& m_ScriptInterface;
CScriptValRooted m_Instance;
JS::PersistentRootedValue m_Instance;
bool m_HasCustomSerialize;
bool m_HasCustomDeserialize;
bool m_HasNullSerialize;

View File

@ -55,9 +55,11 @@ static u8 GetArrayType(JSArrayBufferViewType arrayType)
}
CBinarySerializerScriptImpl::CBinarySerializerScriptImpl(ScriptInterface& scriptInterface, ISerializer& serializer) :
m_ScriptInterface(scriptInterface), m_Serializer(serializer), m_Rooter(m_ScriptInterface),
m_ScriptBackrefsArena(1 * MiB), m_ScriptBackrefs(backrefs_t::key_compare(), ScriptBackrefsAlloc(m_ScriptBackrefsArena)), m_ScriptBackrefsNext(1)
m_ScriptInterface(scriptInterface), m_Serializer(serializer), m_ScriptBackrefs(scriptInterface.GetRuntime()),
m_SerializablePrototypes(new ObjectIdCache<std::wstring>(scriptInterface.GetRuntime())), m_ScriptBackrefsNext(1)
{
m_ScriptBackrefs.init();
m_SerializablePrototypes->init();
}
void CBinarySerializerScriptImpl::HandleScriptVal(JS::HandleValue val)
@ -119,7 +121,7 @@ void CBinarySerializerScriptImpl::HandleScriptVal(JS::HandleValue val)
// Now handle its array buffer
// this may be a backref, since ArrayBuffers can be shared by multiple views
JS::RootedValue bufferVal(cx, JS::ObjectValue(*JS_GetArrayBufferViewBuffer(obj)));
JS::RootedValue bufferVal(cx, JS::ObjectValue(*JS_GetArrayBufferViewBuffer(cx, obj)));
HandleScriptVal(bufferVal);
break;
}
@ -139,7 +141,7 @@ void CBinarySerializerScriptImpl::HandleScriptVal(JS::HandleValue val)
else
{
// Find type of object
JSClass* jsclass = JS_GetClass(obj);
const JSClass* jsclass = JS_GetClass(obj);
if (!jsclass)
throw PSERROR_Serialize_ScriptError("JS_GetClass failed");
JSProtoKey protokey = JSCLASS_CACHED_PROTO_KEY(jsclass);
@ -148,11 +150,11 @@ void CBinarySerializerScriptImpl::HandleScriptVal(JS::HandleValue val)
{
// Object class - check for user-defined prototype
JS::RootedObject proto(cx);
JS_GetPrototype(cx, obj, proto.address());
JS_GetPrototype(cx, obj, &proto);
if (!proto)
throw PSERROR_Serialize_ScriptError("JS_GetPrototype failed");
if (m_SerializablePrototypes.empty() || !IsSerializablePrototype(proto))
if (m_SerializablePrototypes->empty() || !IsSerializablePrototype(proto))
{
// Standard Object prototype
m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_OBJECT);
@ -165,19 +167,19 @@ void CBinarySerializerScriptImpl::HandleScriptVal(JS::HandleValue val)
// User-defined custom prototype
m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_OBJECT_PROTOTYPE);
const std::wstring& prototypeName = GetPrototypeName(proto);
const std::wstring prototypeName = GetPrototypeName(proto);
m_Serializer.String("proto name", prototypeName, 0, 256);
// Does it have custom Serialize function?
// if so, we serialize the data it returns, rather than the object's properties directly
JSBool hasCustomSerialize;
bool hasCustomSerialize;
if (!JS_HasProperty(cx, obj, "Serialize", &hasCustomSerialize))
throw PSERROR_Serialize_ScriptError("JS_HasProperty failed");
if (hasCustomSerialize)
{
JS::RootedValue serialize(cx);
if (!JS_LookupProperty(cx, obj, "Serialize", serialize.address()))
if (!JS_LookupProperty(cx, obj, "Serialize", &serialize))
throw PSERROR_Serialize_ScriptError("JS_LookupProperty failed");
// If serialize is null, so don't serialize anything more
@ -208,7 +210,7 @@ void CBinarySerializerScriptImpl::HandleScriptVal(JS::HandleValue val)
// Standard String object
m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_OBJECT_STRING);
// Get primitive value
JSString* str = JS_ValueToString(cx, val);
JS::RootedString str(cx, JS::ToString(cx, val));
if (!str)
throw PSERROR_Serialize_ScriptError("JS_ValueToString failed");
ScriptString("value", str);
@ -239,15 +241,18 @@ void CBinarySerializerScriptImpl::HandleScriptVal(JS::HandleValue val)
m_ScriptInterface.CallFunction(val, "entries", &keyValueIterator);
for (u32 i=0; i<mapSize; ++i)
{
JS::RootedValue currentIterator(cx);
JS::RootedValue keyValuePair(cx);
ENSURE(m_ScriptInterface.CallFunction(keyValueIterator, "next", &keyValuePair));
ENSURE(m_ScriptInterface.CallFunction(keyValueIterator, "next", &currentIterator));
// the Iterator has a property called "value" that contains the key-value pair of the map
m_ScriptInterface.GetProperty(currentIterator, "value", &keyValuePair);
JS::RootedObject keyValuePairObj(cx, &keyValuePair.toObject());
JS::RootedValue key(cx);
JS::RootedValue value(cx);
ENSURE(JS_GetElement(cx, keyValuePairObj, 0, key.address()));
ENSURE(JS_GetElement(cx, keyValuePairObj, 1, value.address()));
ENSURE(JS_GetElement(cx, keyValuePairObj, 0, &key));
ENSURE(JS_GetElement(cx, keyValuePairObj, 1, &value));
HandleScriptVal(key);
HandleScriptVal(value);
@ -276,15 +281,15 @@ void CBinarySerializerScriptImpl::HandleScriptVal(JS::HandleValue val)
for (size_t i = 0; i < ida.length(); ++i)
{
jsid id = ida[i];
JS::RootedId id(cx, ida[i]);
JS::RootedValue idval(cx);
JS::RootedValue propval(cx);
// Get the property name as a string
if (!JS_IdToValue(cx, id, idval.address()))
if (!JS_IdToValue(cx, id, &idval))
throw PSERROR_Serialize_ScriptError("JS_IdToValue failed");
JSString* idstr = JS_ValueToString(cx, idval.get());
JS::RootedString idstr(cx, JS::ToString(cx, idval));
if (!idstr)
throw PSERROR_Serialize_ScriptError("JS_ValueToString failed");
@ -292,7 +297,7 @@ void CBinarySerializerScriptImpl::HandleScriptVal(JS::HandleValue val)
// Use LookupProperty instead of GetProperty to avoid the danger of getters
// (they might delete values and trigger GC)
if (!JS_LookupPropertyById(cx, obj, id, propval.address()))
if (!JS_LookupPropertyById(cx, obj, id, &propval))
throw PSERROR_Serialize_ScriptError("JS_LookupPropertyById failed");
HandleScriptVal(propval);
@ -304,10 +309,10 @@ void CBinarySerializerScriptImpl::HandleScriptVal(JS::HandleValue val)
{
// We can't serialise functions, but we can at least name the offender (hopefully)
std::wstring funcname(L"(unnamed)");
JSFunction* func = JS_ValueToFunction(cx, val);
JS::RootedFunction func(cx, JS_ValueToFunction(cx, val));
if (func)
{
JSString* string = JS_GetFunctionId(func);
JS::RootedString string(cx, JS_GetFunctionId(func));
if (string)
{
size_t length;
@ -323,7 +328,8 @@ void CBinarySerializerScriptImpl::HandleScriptVal(JS::HandleValue val)
case JSTYPE_STRING:
{
m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_STRING);
ScriptString("string", val.toString());
JS::RootedString stringVal(cx, val.toString());
ScriptString("string", stringVal);
break;
}
case JSTYPE_NUMBER:
@ -365,7 +371,7 @@ void CBinarySerializerScriptImpl::HandleScriptVal(JS::HandleValue val)
}
}
void CBinarySerializerScriptImpl::ScriptString(const char* name, JSString* string)
void CBinarySerializerScriptImpl::ScriptString(const char* name, JS::HandleString string)
{
JSContext* cx = m_ScriptInterface.GetContext();
JSAutoRequest rq(cx);
@ -385,7 +391,7 @@ void CBinarySerializerScriptImpl::ScriptString(const char* name, JSString* strin
m_Serializer.RawBytes(name, (const u8*)chars, length*2);
}
u32 CBinarySerializerScriptImpl::GetScriptBackrefTag(JSObject* obj)
u32 CBinarySerializerScriptImpl::GetScriptBackrefTag(JS::HandleObject obj)
{
// To support non-tree structures (e.g. "var x = []; var y = [x, x];"), we need a way
// to indicate multiple references to one object(/array). So every time we serialize a
@ -395,33 +401,32 @@ u32 CBinarySerializerScriptImpl::GetScriptBackrefTag(JSObject* obj)
// The tags are stored in a map. Maybe it'd be more efficient to store it inline in the object
// somehow? but this works okay for now
std::pair<backrefs_t::iterator, bool> it = m_ScriptBackrefs.insert(std::make_pair(obj, m_ScriptBackrefsNext));
// If it was already there, return the tag
if (!it.second)
return it.first->second;
u32 tag;
if (m_ScriptBackrefs.find(obj, tag))
return tag;
m_ScriptBackrefs.add(m_ScriptInterface.GetContext(), obj, m_ScriptBackrefsNext);
// If it was newly inserted, we need to make sure it gets rooted
// for the duration that it's in m_ScriptBackrefs
m_Rooter.Push(it.first->first);
m_ScriptBackrefsNext++;
// Return a non-tag number so callers know they need to serialize the object
return 0;
}
bool CBinarySerializerScriptImpl::IsSerializablePrototype(JSObject* prototype)
bool CBinarySerializerScriptImpl::IsSerializablePrototype(JS::HandleObject prototype)
{
return m_SerializablePrototypes.find(prototype) != m_SerializablePrototypes.end();
return m_SerializablePrototypes->has(prototype);
}
std::wstring CBinarySerializerScriptImpl::GetPrototypeName(JSObject* prototype)
std::wstring CBinarySerializerScriptImpl::GetPrototypeName(JS::HandleObject prototype)
{
std::map<JSObject*, std::wstring>::iterator it = m_SerializablePrototypes.find(prototype);
ENSURE(it != m_SerializablePrototypes.end());
return it->second;
std::wstring ret;
bool found = m_SerializablePrototypes->find(prototype, ret);
ENSURE(found);
return ret;
}
void CBinarySerializerScriptImpl::SetSerializablePrototypes(std::map<JSObject*, std::wstring>& prototypes)
void CBinarySerializerScriptImpl::SetSerializablePrototypes(shared_ptr<ObjectIdCache<std::wstring> > prototypes)
{
m_SerializablePrototypes = prototypes;
}

View File

@ -20,7 +20,7 @@
#include "ISerializer.h"
#include "scriptinterface/AutoRooters.h"
#include "scriptinterface/third_party/ObjectToIDMap.h"
#include "lib/byte_order.h"
#include "lib/allocators/arena.h"
@ -57,28 +57,21 @@ class CBinarySerializerScriptImpl
public:
CBinarySerializerScriptImpl(ScriptInterface& scriptInterface, ISerializer& serializer);
void ScriptString(const char* name, JSString* string);
void ScriptString(const char* name, JS::HandleString string);
void HandleScriptVal(JS::HandleValue val);
void SetSerializablePrototypes(std::map<JSObject*, std::wstring>& prototypes);
void SetSerializablePrototypes(shared_ptr<ObjectIdCache<std::wstring> > prototypes);
private:
ScriptInterface& m_ScriptInterface;
ISerializer& m_Serializer;
// Pooling helps since we do a lot of short-lived allocations
typedef ProxyAllocator<std::pair<JSObject* const, u32>, Allocators::DynamicArena> ScriptBackrefsAlloc;
typedef std::map<JSObject*, u32, std::less<JSObject*>, ScriptBackrefsAlloc> backrefs_t;
Allocators::DynamicArena m_ScriptBackrefsArena;
backrefs_t m_ScriptBackrefs;
ObjectIdCache<u32> m_ScriptBackrefs;
u32 m_ScriptBackrefsNext;
u32 GetScriptBackrefTag(JSObject* obj);
u32 GetScriptBackrefTag(JS::HandleObject obj);
AutoGCRooter m_Rooter;
shared_ptr<ObjectIdCache<std::wstring> > m_SerializablePrototypes;
std::map<JSObject*, std::wstring> m_SerializablePrototypes;
bool IsSerializablePrototype(JSObject* prototype);
std::wstring GetPrototypeName(JSObject* prototype);
bool IsSerializablePrototype(JS::HandleObject prototype);
std::wstring GetPrototypeName(JS::HandleObject prototype);
};
/**
@ -106,7 +99,7 @@ public:
{
}
virtual void SetSerializablePrototypes(std::map<JSObject*, std::wstring>& prototypes)
virtual void SetSerializablePrototypes(shared_ptr<ObjectIdCache<std::wstring> >& prototypes)
{
m_ScriptImpl->SetSerializablePrototypes(prototypes);
}

View File

@ -62,7 +62,7 @@ public:
virtual void ScriptObjectAppend(const char* name, JS::HandleValue objVal) = 0;
/// Deserialize a JSString
virtual void ScriptString(const char* name, JSString*& out) = 0;
virtual void ScriptString(const char* name, JS::MutableHandleString out) = 0;
virtual void RawBytes(const char* name, u8* data, size_t len);

View File

@ -28,13 +28,38 @@
#include "lib/byte_order.h"
CStdDeserializer::CStdDeserializer(ScriptInterface& scriptInterface, std::istream& stream) :
m_ScriptInterface(scriptInterface), m_Stream(stream)
m_ScriptInterface(scriptInterface), m_Stream(stream),
m_dummyObject(scriptInterface.GetJSRuntime())
{
JSContext* cx = m_ScriptInterface.GetContext();
JSAutoRequest rq(cx);
JS_AddExtraGCRootsTracer(m_ScriptInterface.GetJSRuntime(), CStdDeserializer::Trace, this);
// Add a dummy tag because the serializer uses the tag 0 to indicate that a value
// needs to be serialized and then tagged
m_dummyObject.set(JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
m_ScriptBackrefs.push_back(JS::Heap<JSObject*>(m_dummyObject));
}
CStdDeserializer::~CStdDeserializer()
{
FreeScriptBackrefs();
JS_RemoveExtraGCRootsTracer(m_ScriptInterface.GetJSRuntime(), CStdDeserializer::Trace, this);
}
void CStdDeserializer::Trace(JSTracer *trc, void *data)
{
reinterpret_cast<CStdDeserializer*>(data)->TraceMember(trc);
}
void CStdDeserializer::TraceMember(JSTracer *trc)
{
for (size_t i=0; i<m_ScriptBackrefs.size(); i++)
JS_CallHeapObjectTracer(trc, &m_ScriptBackrefs[i], "StdDeserializer::m_ScriptBackrefs");
for (auto& proto : m_SerializablePrototypes)
JS_CallHeapObjectTracer(trc, &proto.second, "StdDeserializer::m_SerializablePrototypes");
}
void CStdDeserializer::Get(const char* name, u8* data, size_t len)
@ -79,44 +104,31 @@ void CStdDeserializer::RequireBytesInStream(size_t numBytes)
throw PSERROR_Deserialize_OutOfBounds("RequireBytesInStream");
}
void CStdDeserializer::AddScriptBackref(JSObject* obj)
void CStdDeserializer::AddScriptBackref(JS::HandleObject obj)
{
std::pair<std::map<u32, JSObject*>::iterator, bool> it = m_ScriptBackrefs.insert(std::make_pair((u32)m_ScriptBackrefs.size()+1, obj));
ENSURE(it.second);
if (!JS_AddObjectRoot(m_ScriptInterface.GetContext(), &it.first->second))
throw PSERROR_Deserialize_ScriptError("JS_AddRoot failed");
m_ScriptBackrefs.push_back(JS::Heap<JSObject*>(obj));
}
JSObject* CStdDeserializer::GetScriptBackref(u32 tag)
void CStdDeserializer::GetScriptBackref(u32 tag, JS::MutableHandleObject ret)
{
std::map<u32, JSObject*>::const_iterator it = m_ScriptBackrefs.find(tag);
if (it == m_ScriptBackrefs.end())
return NULL;
return it->second;
ENSURE(m_ScriptBackrefs.size() > tag);
ret.set(m_ScriptBackrefs[tag]);
}
u32 CStdDeserializer::ReserveScriptBackref()
{
AddScriptBackref(NULL);
return m_ScriptBackrefs.size();
m_ScriptBackrefs.push_back(JS::Heap<JSObject*>(m_dummyObject));
return m_ScriptBackrefs.size()-1;
}
void CStdDeserializer::SetReservedScriptBackref(u32 tag, JSObject* obj)
void CStdDeserializer::SetReservedScriptBackref(u32 tag, JS::HandleObject obj)
{
std::pair<std::map<u32, JSObject*>::iterator, bool> it = m_ScriptBackrefs.insert(std::make_pair(tag, obj));
ENSURE(!it.second);
ENSURE(m_ScriptBackrefs[tag] == m_dummyObject);
m_ScriptBackrefs[tag] = JS::Heap<JSObject*>(obj);
}
void CStdDeserializer::FreeScriptBackrefs()
{
JSContext* cx = m_ScriptInterface.GetContext();
JSAutoRequest rq(cx);
std::map<u32, JSObject*>::iterator it = m_ScriptBackrefs.begin();
for (; it != m_ScriptBackrefs.end(); ++it)
{
JS_RemoveObjectRoot(m_ScriptInterface.GetContext(), &it->second);
}
m_ScriptBackrefs.clear();
}
@ -132,10 +144,10 @@ jsval CStdDeserializer::ReadScriptVal(const char* UNUSED(name), JS::HandleObject
switch (type)
{
case SCRIPT_TYPE_VOID:
return JSVAL_VOID;
return JS::UndefinedValue();
case SCRIPT_TYPE_NULL:
return JSVAL_NULL;
return JS::NullValue();
case SCRIPT_TYPE_ARRAY:
case SCRIPT_TYPE_OBJECT:
@ -150,11 +162,11 @@ jsval CStdDeserializer::ReadScriptVal(const char* UNUSED(name), JS::HandleObject
{
u32 length;
NumberU32_Unbounded("array length", length);
obj.set(JS_NewArrayObject(cx, length, NULL));
obj.set(JS_NewArrayObject(cx, length));
}
else if (type == SCRIPT_TYPE_OBJECT)
{
obj.set(JS_NewObject(cx, NULL, NULL, NULL));
obj.set(JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
}
else // SCRIPT_TYPE_OBJECT_PROTOTYPE
{
@ -162,7 +174,8 @@ jsval CStdDeserializer::ReadScriptVal(const char* UNUSED(name), JS::HandleObject
String("proto name", prototypeName, 0, 256);
// Get constructor object
JS::RootedObject proto(cx, GetSerializablePrototype(prototypeName));
JS::RootedObject proto(cx);
GetSerializablePrototype(prototypeName, &proto);
if (!proto)
throw PSERROR_Deserialize_ScriptError("Failed to find serializable prototype for object");
@ -170,22 +183,22 @@ jsval CStdDeserializer::ReadScriptVal(const char* UNUSED(name), JS::HandleObject
if (!proto || !parent)
throw PSERROR_Deserialize_ScriptError();
obj.set(JS_NewObject(cx, NULL, proto, parent));
obj.set(JS_NewObject(cx, nullptr, proto, parent));
if (!obj)
throw PSERROR_Deserialize_ScriptError("JS_NewObject failed");
// Does it have custom Deserialize function?
// if so, we let it handle the deserialized data, rather than adding properties directly
JSBool hasCustomDeserialize, hasCustomSerialize;
bool hasCustomDeserialize, hasCustomSerialize;
if (!JS_HasProperty(cx, obj, "Serialize", &hasCustomSerialize) || !JS_HasProperty(cx, obj, "Deserialize", &hasCustomDeserialize))
throw PSERROR_Serialize_ScriptError("JS_HasProperty failed");
if (hasCustomDeserialize)
{
JS::RootedValue serialize(cx);
if (!JS_LookupProperty(cx, obj, "Serialize", serialize.address()))
if (!JS_LookupProperty(cx, obj, "Serialize", &serialize))
throw PSERROR_Serialize_ScriptError("JS_LookupProperty failed");
bool hasNullSerialize = hasCustomSerialize && JSVAL_IS_NULL(serialize);
bool hasNullSerialize = hasCustomSerialize && serialize.isNull();
// If Serialize is null, we'll still call Deserialize but with undefined argument
JS::RootedValue data(cx);
@ -203,7 +216,6 @@ jsval CStdDeserializer::ReadScriptVal(const char* UNUSED(name), JS::HandleObject
if (!obj)
throw PSERROR_Deserialize_ScriptError("Deserializer failed to create new object");
CScriptValRooted objRoot(cx, JS::ObjectValue(*obj));
AddScriptBackref(obj);
@ -216,7 +228,7 @@ jsval CStdDeserializer::ReadScriptVal(const char* UNUSED(name), JS::HandleObject
ReadStringUTF16("prop name", propname);
JS::RootedValue propval(cx, ReadScriptVal("prop value", JS::NullPtr()));
if (!JS_SetUCProperty(cx, obj, (const jschar*)propname.data(), propname.length(), propval.address()))
if (!JS_SetUCProperty(cx, obj, (const jschar*)propname.data(), propname.length(), propval))
throw PSERROR_Deserialize_ScriptError();
}
@ -224,8 +236,8 @@ jsval CStdDeserializer::ReadScriptVal(const char* UNUSED(name), JS::HandleObject
}
case SCRIPT_TYPE_STRING:
{
JSString* str;
ScriptString("string", str);
JS::RootedString str(cx);
ScriptString("string", &str);
return JS::StringValue(str);
}
case SCRIPT_TYPE_INT:
@ -238,8 +250,8 @@ jsval CStdDeserializer::ReadScriptVal(const char* UNUSED(name), JS::HandleObject
{
double value;
NumberDouble_Unbounded("value", value);
jsval rval = JS::NumberValue(value);
if (JSVAL_IS_NULL(rval))
JS::RootedValue rval(cx, JS::NumberValue(value));
if (rval.isNull())
throw PSERROR_Deserialize_ScriptError("JS_NewNumberValue failed");
return rval;
}
@ -253,7 +265,8 @@ jsval CStdDeserializer::ReadScriptVal(const char* UNUSED(name), JS::HandleObject
{
u32 tag;
NumberU32_Unbounded("tag", tag);
JSObject* obj = GetScriptBackref(tag);
JS::RootedObject obj(cx);
GetScriptBackref(tag, &obj);
if (!obj)
throw PSERROR_Deserialize_ScriptError("Invalid backref tag");
return JS::ObjectValue(*obj);
@ -263,13 +276,12 @@ jsval CStdDeserializer::ReadScriptVal(const char* UNUSED(name), JS::HandleObject
double value;
NumberDouble_Unbounded("value", value);
JS::RootedValue val(cx, JS::NumberValue(value));
CScriptValRooted objRoot(cx, val);
JSObject* ctorobj;
if (!JS_GetClassObject(cx, JS_GetGlobalForScopeChain(cx), JSProto_Number, &ctorobj))
JS::RootedObject ctorobj(cx);
if (!JS_GetClassObject(cx, JSProto_Number, &ctorobj))
throw PSERROR_Deserialize_ScriptError("JS_GetClassObject failed");
JSObject* obj = JS_New(cx, ctorobj, 1, val.address());
JS::RootedObject obj(cx, JS_New(cx, ctorobj, val));
if (!obj)
throw PSERROR_Deserialize_ScriptError("JS_New failed");
AddScriptBackref(obj);
@ -277,18 +289,17 @@ jsval CStdDeserializer::ReadScriptVal(const char* UNUSED(name), JS::HandleObject
}
case SCRIPT_TYPE_OBJECT_STRING:
{
JSString* str;
ScriptString("value", str);
JS::RootedString str(cx);
ScriptString("value", &str);
if (!str)
throw PSERROR_Deserialize_ScriptError();
JS::RootedValue val(cx, JS::StringValue(str));
CScriptValRooted valRoot(cx, val);
JSObject* ctorobj;
if (!JS_GetClassObject(cx, JS_GetGlobalForScopeChain(cx), JSProto_String, &ctorobj))
JS::RootedObject ctorobj(cx);
if (!JS_GetClassObject(cx, JSProto_String, &ctorobj))
throw PSERROR_Deserialize_ScriptError("JS_GetClassObject failed");
JSObject* obj = JS_New(cx, ctorobj, 1, val.address());
JS::RootedObject obj(cx, JS_New(cx, ctorobj, val));
if (!obj)
throw PSERROR_Deserialize_ScriptError("JS_New failed");
AddScriptBackref(obj);
@ -300,11 +311,11 @@ jsval CStdDeserializer::ReadScriptVal(const char* UNUSED(name), JS::HandleObject
Bool("value", value);
JS::RootedValue val(cx, JS::BooleanValue(value));
JSObject* ctorobj;
if (!JS_GetClassObject(cx, JS_GetGlobalForScopeChain(cx), JSProto_Boolean, &ctorobj))
JS::RootedObject ctorobj(cx);
if (!JS_GetClassObject(cx, JSProto_Boolean, &ctorobj))
throw PSERROR_Deserialize_ScriptError("JS_GetClassObject failed");
JSObject* obj = JS_New(cx, ctorobj, 1, val.address());
JS::RootedObject obj(cx, JS_New(cx, ctorobj, val));
if (!obj)
throw PSERROR_Deserialize_ScriptError("JS_New failed");
AddScriptBackref(obj);
@ -322,16 +333,16 @@ jsval CStdDeserializer::ReadScriptVal(const char* UNUSED(name), JS::HandleObject
u32 arrayTag = ReserveScriptBackref();
// Get buffer object
jsval bufferVal = ReadScriptVal("buffer", JS::NullPtr());
JS::RootedValue bufferVal(cx, ReadScriptVal("buffer", JS::NullPtr()));
if (!bufferVal.isObject())
throw PSERROR_Deserialize_ScriptError();
JSObject* bufferObj = &bufferVal.toObject();
JS::RootedObject bufferObj(cx, &bufferVal.toObject());
if (!JS_IsArrayBufferObject(bufferObj))
throw PSERROR_Deserialize_ScriptError("js_IsArrayBuffer failed");
JSObject* arrayObj;
JS::RootedObject arrayObj(cx);
switch(arrayType)
{
case SCRIPT_TYPED_ARRAY_INT8:
@ -375,16 +386,14 @@ jsval CStdDeserializer::ReadScriptVal(const char* UNUSED(name), JS::HandleObject
{
u32 length;
NumberU32_Unbounded("buffer length", length);
u8* bufferData = NULL;
#if BYTE_ORDER != LITTLE_ENDIAN
#error TODO: need to convert JS ArrayBuffer data from little-endian
#endif
void* contents = NULL;
JS_AllocateArrayBufferContents(cx, length, &contents, &bufferData);
RawBytes("buffer data", bufferData, length);
JSObject* bufferObj = JS_NewArrayBufferWithContents(cx, contents);
contents = JS_AllocateArrayBufferContents(cx, length);
RawBytes("buffer data", (u8*)contents, length);
JS::RootedObject bufferObj(cx, JS_NewArrayBufferWithContents(cx, length, contents));
AddScriptBackref(bufferObj);
return JS::ObjectValue(*bufferObj);
@ -402,7 +411,8 @@ jsval CStdDeserializer::ReadScriptVal(const char* UNUSED(name), JS::HandleObject
JS::RootedValue value(cx, ReadScriptVal("map value", JS::NullPtr()));
m_ScriptInterface.CallFunctionVoid(mapVal, "set", key, value);
}
AddScriptBackref(&mapVal.toObject());
JS::RootedObject mapObj(cx, &mapVal.toObject());
AddScriptBackref(mapObj);
return mapVal;
}
default:
@ -419,7 +429,7 @@ void CStdDeserializer::ReadStringUTF16(const char* name, utf16string& str)
Get(name, (u8*)str.data(), len*2);
}
void CStdDeserializer::ScriptString(const char* name, JSString*& out)
void CStdDeserializer::ScriptString(const char* name, JS::MutableHandleString out)
{
utf16string str;
ReadStringUTF16(name, str);
@ -428,7 +438,7 @@ void CStdDeserializer::ScriptString(const char* name, JSString*& out)
#error TODO: probably need to convert JS strings from little-endian
#endif
out = JS_NewUCStringCopyN(m_ScriptInterface.GetContext(), (const jschar*)str.data(), str.length());
out.set(JS_NewUCStringCopyN(m_ScriptInterface.GetContext(), (const jschar*)str.data(), str.length()));
if (!out)
throw PSERROR_Deserialize_ScriptError("JS_NewUCStringCopyN failed");
}
@ -450,7 +460,7 @@ void CStdDeserializer::ScriptObjectAppend(const char* name, JS::HandleValue objV
ReadScriptVal(name, obj);
}
void CStdDeserializer::SetSerializablePrototypes(std::map<std::wstring, JSObject*>& prototypes)
void CStdDeserializer::SetSerializablePrototypes(std::map<std::wstring, JS::Heap<JSObject*> >& prototypes)
{
m_SerializablePrototypes = prototypes;
}
@ -460,11 +470,11 @@ bool CStdDeserializer::IsSerializablePrototype(const std::wstring& name)
return m_SerializablePrototypes.find(name) != m_SerializablePrototypes.end();
}
JSObject* CStdDeserializer::GetSerializablePrototype(const std::wstring& name)
void CStdDeserializer::GetSerializablePrototype(const std::wstring& name, JS::MutableHandleObject ret)
{
std::map<std::wstring, JSObject*>::iterator it = m_SerializablePrototypes.find(name);
std::map<std::wstring, JS::Heap<JSObject*> >::iterator it = m_SerializablePrototypes.find(name);
if (it != m_SerializablePrototypes.end())
return it->second;
ret.set(it->second);
else
return NULL;
ret.set(NULL);
}

View File

@ -33,13 +33,17 @@ public:
virtual void ScriptVal(const char* name, JS::MutableHandleValue out);
virtual void ScriptObjectAppend(const char* name, JS::HandleValue objVal);
virtual void ScriptString(const char* name, JSString*& out);
virtual void ScriptString(const char* name, JS::MutableHandleString out);
virtual std::istream& GetStream();
virtual void RequireBytesInStream(size_t numBytes);
virtual void SetSerializablePrototypes(std::map<std::wstring, JSObject*>& prototypes);
virtual void SetSerializablePrototypes(std::map<std::wstring, JS::Heap<JSObject*> >& prototypes);
static void Trace(JSTracer *trc, void *data);
void TraceMember(JSTracer *trc);
protected:
virtual void Get(const char* name, u8* data, size_t len);
@ -47,20 +51,22 @@ private:
jsval ReadScriptVal(const char* name, JS::HandleObject appendParent);
void ReadStringUTF16(const char* name, utf16string& str);
virtual void AddScriptBackref(JSObject* obj);
virtual JSObject* GetScriptBackref(u32 tag);
virtual void AddScriptBackref(JS::HandleObject obj);
virtual void GetScriptBackref(u32 tag, JS::MutableHandleObject ret);
virtual u32 ReserveScriptBackref();
virtual void SetReservedScriptBackref(u32 tag, JSObject* obj);
virtual void SetReservedScriptBackref(u32 tag, JS::HandleObject obj);
void FreeScriptBackrefs();
std::map<u32, JSObject*> m_ScriptBackrefs; // vector would be nice but maintaining JS roots would be harder
std::vector<JS::Heap<JSObject*> > m_ScriptBackrefs;
JS::PersistentRooted<JSObject*> m_dummyObject;
ScriptInterface& m_ScriptInterface;
std::istream& m_Stream;
std::map<std::wstring, JSObject*> m_SerializablePrototypes;
std::map<std::wstring, JS::Heap<JSObject*> > m_SerializablePrototypes;
bool IsSerializablePrototype(const std::wstring& name);
JSObject* GetSerializablePrototype(const std::wstring& name);
void GetSerializablePrototype(const std::wstring& name, JS::MutableHandleObject ret);
};
#endif // INCLUDED_STDDESERIALIZER

View File

@ -42,7 +42,7 @@
}
#define DEFAULT_COMPONENT_ALLOCATOR(cname) \
static IComponent* Allocate(ScriptInterface&, jsval) { return new CCmp##cname(); } \
static IComponent* Allocate(ScriptInterface&, JS::HandleValue) { return new CCmp##cname(); } \
static void Deallocate(IComponent* cmp) { delete static_cast<CCmp##cname*> (cmp); } \
virtual int GetComponentTypeId() const \
{ \
@ -51,7 +51,7 @@
#define DEFAULT_SCRIPT_WRAPPER(cname) \
static void ClassInit(CComponentManager& UNUSED(componentManager)) { } \
static IComponent* Allocate(ScriptInterface& scriptInterface, jsval instance) \
static IComponent* Allocate(ScriptInterface& scriptInterface, JS::HandleValue instance) \
{ \
return new CCmp##cname(scriptInterface, instance); \
} \
@ -59,7 +59,7 @@
{ \
delete static_cast<CCmp##cname*> (cmp); \
} \
CCmp##cname(ScriptInterface& scriptInterface, jsval instance) : m_Script(scriptInterface, instance) { } \
CCmp##cname(ScriptInterface& scriptInterface, JS::HandleValue instance) : m_Script(scriptInterface, instance) { } \
static std::string GetSchema() \
{ \
return "<a:component type='script-wrapper'/><empty/>"; \
@ -84,7 +84,7 @@
{ \
m_Script.Deserialize(paramNode, deserialize, GetEntityId()); \
} \
virtual jsval GetJSInstance() const \
virtual JS::Value GetJSInstance() const \
{ \
return m_Script.GetInstance(); \
} \

View File

@ -40,17 +40,17 @@ public:
virtual int GetType() const { return mtid; }
virtual const char* GetScriptHandlerName() const { return handlerName.c_str(); }
virtual const char* GetScriptGlobalHandlerName() const { return globalHandlerName.c_str(); }
virtual jsval ToJSVal(ScriptInterface& UNUSED(scriptInterface)) const { return msg.get(); }
virtual JS::Value ToJSVal(ScriptInterface& UNUSED(scriptInterface)) const { return msg.get(); }
CMessageScripted(ScriptInterface& scriptInterface, int mtid, const std::string& name, JS::HandleValue msg) :
mtid(mtid), handlerName("On" + name), globalHandlerName("OnGlobal" + name), msg(scriptInterface.GetContext(), msg)
mtid(mtid), handlerName("On" + name), globalHandlerName("OnGlobal" + name), msg(scriptInterface.GetJSRuntime(), msg)
{
}
int mtid;
std::string handlerName;
std::string globalHandlerName;
CScriptValRooted msg;
JS::PersistentRootedValue msg;
};
CComponentManager::CComponentManager(CSimContext& context, shared_ptr<ScriptRuntime> rt, bool skipScriptFunctions) :
@ -70,23 +70,23 @@ CComponentManager::CComponentManager(CSimContext& context, shared_ptr<ScriptRunt
// these functions, so we skip registering them here in those cases
if (!skipScriptFunctions)
{
m_ScriptInterface.RegisterFunction<void, int, std::string, CScriptVal, CComponentManager::Script_RegisterComponentType> ("RegisterComponentType");
m_ScriptInterface.RegisterFunction<void, int, std::string, CScriptVal, CComponentManager::Script_RegisterSystemComponentType> ("RegisterSystemComponentType");
m_ScriptInterface.RegisterFunction<void, int, std::string, CScriptVal, CComponentManager::Script_ReRegisterComponentType> ("ReRegisterComponentType");
m_ScriptInterface.RegisterFunction<void, int, std::string, JS::HandleValue, CComponentManager::Script_RegisterComponentType> ("RegisterComponentType");
m_ScriptInterface.RegisterFunction<void, int, std::string, JS::HandleValue, CComponentManager::Script_RegisterSystemComponentType> ("RegisterSystemComponentType");
m_ScriptInterface.RegisterFunction<void, int, std::string, JS::HandleValue, CComponentManager::Script_ReRegisterComponentType> ("ReRegisterComponentType");
m_ScriptInterface.RegisterFunction<void, std::string, CComponentManager::Script_RegisterInterface> ("RegisterInterface");
m_ScriptInterface.RegisterFunction<void, std::string, CComponentManager::Script_RegisterMessageType> ("RegisterMessageType");
m_ScriptInterface.RegisterFunction<void, std::string, CScriptVal, CComponentManager::Script_RegisterGlobal> ("RegisterGlobal");
m_ScriptInterface.RegisterFunction<void, std::string, JS::HandleValue, CComponentManager::Script_RegisterGlobal> ("RegisterGlobal");
m_ScriptInterface.RegisterFunction<IComponent*, int, int, CComponentManager::Script_QueryInterface> ("QueryInterface");
m_ScriptInterface.RegisterFunction<std::vector<int>, int, CComponentManager::Script_GetEntitiesWithInterface> ("GetEntitiesWithInterface");
m_ScriptInterface.RegisterFunction<std::vector<IComponent*>, int, CComponentManager::Script_GetComponentsWithInterface> ("GetComponentsWithInterface");
m_ScriptInterface.RegisterFunction<void, int, int, CScriptVal, CComponentManager::Script_PostMessage> ("PostMessage");
m_ScriptInterface.RegisterFunction<void, int, CScriptVal, CComponentManager::Script_BroadcastMessage> ("BroadcastMessage");
m_ScriptInterface.RegisterFunction<void, int, int, JS::HandleValue, CComponentManager::Script_PostMessage> ("PostMessage");
m_ScriptInterface.RegisterFunction<void, int, JS::HandleValue, CComponentManager::Script_BroadcastMessage> ("BroadcastMessage");
m_ScriptInterface.RegisterFunction<int, std::string, CComponentManager::Script_AddEntity> ("AddEntity");
m_ScriptInterface.RegisterFunction<int, std::string, CComponentManager::Script_AddLocalEntity> ("AddLocalEntity");
m_ScriptInterface.RegisterFunction<void, int, CComponentManager::Script_DestroyEntity> ("DestroyEntity");
m_ScriptInterface.RegisterFunction<void, CComponentManager::Script_FlushDestroyedEntities> ("FlushDestroyedEntities");
m_ScriptInterface.RegisterFunction<CScriptVal, std::wstring, CComponentManager::Script_ReadJSONFile> ("ReadJSONFile");
m_ScriptInterface.RegisterFunction<CScriptVal, std::wstring, CComponentManager::Script_ReadCivJSONFile> ("ReadCivJSONFile");
m_ScriptInterface.RegisterFunction<JS::Value, std::wstring, CComponentManager::Script_ReadJSONFile> ("ReadJSONFile");
m_ScriptInterface.RegisterFunction<JS::Value, std::wstring, CComponentManager::Script_ReadCivJSONFile> ("ReadCivJSONFile");
m_ScriptInterface.RegisterFunction<std::vector<std::string>, std::wstring, bool, CComponentManager::Script_FindJSONFiles> ("FindJSONFiles");
}
@ -148,13 +148,11 @@ bool CComponentManager::LoadScript(const VfsPath& filename, bool hotload)
return ok;
}
void CComponentManager::Script_RegisterComponentType_Common(ScriptInterface::CxPrivate* pCxPrivate, int iid, std::string cname, CScriptVal ctor1, bool reRegister, bool systemComponent)
void CComponentManager::Script_RegisterComponentType_Common(ScriptInterface::CxPrivate* pCxPrivate, int iid, std::string cname, JS::HandleValue ctor, bool reRegister, bool systemComponent)
{
CComponentManager* componentManager = static_cast<CComponentManager*> (pCxPrivate->pCBData);
JSContext* cx = componentManager->m_ScriptInterface.GetContext();
JSAutoRequest rq(cx);
JS::RootedValue ctor(cx, ctor1.get()); // TODO: Get Handle parameter directly with SpiderMonkey 31
// Find the C++ component that wraps the interface
int cidWrapper = componentManager->GetScriptWrapper(iid);
@ -246,16 +244,15 @@ void CComponentManager::Script_RegisterComponentType_Common(ScriptInterface::CxP
}
// Construct a new ComponentType, using the wrapper's alloc functions
ComponentType ct = {
componentManager->m_ComponentTypesById[cid] = {
CT_Script,
iid,
ctWrapper.alloc,
ctWrapper.dealloc,
cname,
schema,
CScriptValRooted(cx, ctor)
DefPersistentRooted<JS::Value>(cx, ctor)
};
componentManager->m_ComponentTypesById[cid] = ct;
componentManager->m_CurrentComponent = cid; // needed by Subscribe
@ -320,21 +317,21 @@ void CComponentManager::Script_RegisterComponentType_Common(ScriptInterface::CxP
}
}
void CComponentManager::Script_RegisterComponentType(ScriptInterface::CxPrivate* pCxPrivate, int iid, std::string cname, CScriptVal ctor)
void CComponentManager::Script_RegisterComponentType(ScriptInterface::CxPrivate* pCxPrivate, int iid, std::string cname, JS::HandleValue ctor)
{
CComponentManager* componentManager = static_cast<CComponentManager*> (pCxPrivate->pCBData);
componentManager->Script_RegisterComponentType_Common(pCxPrivate, iid, cname, ctor, false, false);
componentManager->m_ScriptInterface.SetGlobal(cname.c_str(), ctor, componentManager->m_CurrentlyHotloading);
}
void CComponentManager::Script_RegisterSystemComponentType(ScriptInterface::CxPrivate* pCxPrivate, int iid, std::string cname, CScriptVal ctor)
void CComponentManager::Script_RegisterSystemComponentType(ScriptInterface::CxPrivate* pCxPrivate, int iid, std::string cname, JS::HandleValue ctor)
{
CComponentManager* componentManager = static_cast<CComponentManager*> (pCxPrivate->pCBData);
componentManager->Script_RegisterComponentType_Common(pCxPrivate, iid, cname, ctor, false, true);
componentManager->m_ScriptInterface.SetGlobal(cname.c_str(), ctor, componentManager->m_CurrentlyHotloading);
}
void CComponentManager::Script_ReRegisterComponentType(ScriptInterface::CxPrivate* pCxPrivate, int iid, std::string cname, CScriptVal ctor)
void CComponentManager::Script_ReRegisterComponentType(ScriptInterface::CxPrivate* pCxPrivate, int iid, std::string cname, JS::HandleValue ctor)
{
Script_RegisterComponentType_Common(pCxPrivate, iid, cname, ctor, true, false);
}
@ -386,7 +383,7 @@ void CComponentManager::Script_RegisterMessageType(ScriptInterface::CxPrivate* p
componentManager->m_ScriptInterface.SetGlobal(("MT_" + name).c_str(), (int)id);
}
void CComponentManager::Script_RegisterGlobal(ScriptInterface::CxPrivate* pCxPrivate, std::string name, CScriptVal value)
void CComponentManager::Script_RegisterGlobal(ScriptInterface::CxPrivate* pCxPrivate, std::string name, JS::HandleValue value)
{
CComponentManager* componentManager = static_cast<CComponentManager*> (pCxPrivate->pCBData);
@ -441,14 +438,9 @@ CMessage* CComponentManager::ConstructMessage(int mtid, JS::HandleValue data)
}
}
void CComponentManager::Script_PostMessage(ScriptInterface::CxPrivate* pCxPrivate, int ent, int mtid, CScriptVal data1)
void CComponentManager::Script_PostMessage(ScriptInterface::CxPrivate* pCxPrivate, int ent, int mtid, JS::HandleValue data)
{
CComponentManager* componentManager = static_cast<CComponentManager*> (pCxPrivate->pCBData);
JSContext* cx = componentManager->GetScriptInterface().GetContext();
JSAutoRequest rq(cx);
// TODO: With ESR31 we should be able to take JS::HandleValue directly
JS::RootedValue data(cx, data1.get());
CMessage* msg = componentManager->ConstructMessage(mtid, data);
if (!msg)
@ -459,14 +451,9 @@ void CComponentManager::Script_PostMessage(ScriptInterface::CxPrivate* pCxPrivat
delete msg;
}
void CComponentManager::Script_BroadcastMessage(ScriptInterface::CxPrivate* pCxPrivate, int mtid, CScriptVal data1)
void CComponentManager::Script_BroadcastMessage(ScriptInterface::CxPrivate* pCxPrivate, int mtid, JS::HandleValue data)
{
CComponentManager* componentManager = static_cast<CComponentManager*> (pCxPrivate->pCBData);
JSContext* cx = componentManager->GetScriptInterface().GetContext();
JSAutoRequest rq(cx);
// TODO: With ESR31 we should be able to take JS::HandleValue directly
JS::RootedValue data(cx, data1.get());
CMessage* msg = componentManager->ConstructMessage(mtid, data);
if (!msg)
@ -555,16 +542,16 @@ void CComponentManager::ResetState()
void CComponentManager::RegisterComponentType(InterfaceId iid, ComponentTypeId cid, AllocFunc alloc, DeallocFunc dealloc,
const char* name, const std::string& schema)
{
ComponentType c = { CT_Native, iid, alloc, dealloc, name, schema, CScriptValRooted() };
m_ComponentTypesById.insert(std::make_pair(cid, c));
ComponentType c = { CT_Native, iid, alloc, dealloc, name, schema, DefPersistentRooted<JS::Value>() };
m_ComponentTypesById.insert(std::make_pair(cid, std::move(c)));
m_ComponentTypeIdsByName[name] = cid;
}
void CComponentManager::RegisterComponentTypeScriptWrapper(InterfaceId iid, ComponentTypeId cid, AllocFunc alloc,
DeallocFunc dealloc, const char* name, const std::string& schema)
{
ComponentType c = { CT_ScriptWrapper, iid, alloc, dealloc, name, schema, CScriptValRooted() };
m_ComponentTypesById.insert(std::make_pair(cid, c));
ComponentType c = { CT_ScriptWrapper, iid, alloc, dealloc, name, schema, DefPersistentRooted<JS::Value>() };
m_ComponentTypesById.insert(std::make_pair(cid, std::move(c)));
m_ComponentTypeIdsByName[name] = cid;
// TODO: merge with RegisterComponentType
}
@ -772,12 +759,9 @@ IComponent* CComponentManager::ConstructComponent(CEntityHandle ent, ComponentTy
// If this is a scripted component, construct the appropriate JS object first
JS::RootedValue obj(cx);
// TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
JS::RootedValue tmpCtor(cx, ct.ctor.get());
if (ct.type == CT_Script)
{
JS::AutoValueVector argv(cx); // TODO: With SpiderMonkey 31, we can pass JS::HandleValueArray::empty()
m_ScriptInterface.CallConstructor(tmpCtor, argv, &obj);
m_ScriptInterface.CallConstructor(ct.ctor.get(), JS::HandleValueArray::empty(), &obj);
if (obj.isNull())
{
LOGERROR("Script component constructor failed");
@ -1175,12 +1159,12 @@ std::string CComponentManager::GenerateSchema()
return schema;
}
CScriptVal CComponentManager::Script_ReadJSONFile(ScriptInterface::CxPrivate* pCxPrivate, std::wstring fileName)
JS::Value CComponentManager::Script_ReadJSONFile(ScriptInterface::CxPrivate* pCxPrivate, std::wstring fileName)
{
return ReadJSONFile(pCxPrivate, L"simulation/data", fileName);
}
CScriptVal CComponentManager::Script_ReadCivJSONFile(ScriptInterface::CxPrivate* pCxPrivate, std::wstring fileName)
JS::Value CComponentManager::Script_ReadCivJSONFile(ScriptInterface::CxPrivate* pCxPrivate, std::wstring fileName)
{
return ReadJSONFile(pCxPrivate, L"civs", fileName);
}
@ -1194,7 +1178,7 @@ JS::Value CComponentManager::ReadJSONFile(ScriptInterface::CxPrivate* pCxPrivate
VfsPath path = VfsPath(filePath) / fileName;
JS::RootedValue out(cx);
componentManager->GetScriptInterface().ReadJSONFile(path, &out);
return out.get();
return out;
}
Status CComponentManager::FindJSONFilesCallback(const VfsPath& pathname, const CFileInfo& UNUSED(fileInfo), const uintptr_t cbData)

View File

@ -47,7 +47,7 @@ public:
private:
// Component allocation types
typedef IComponent* (*AllocFunc)(ScriptInterface& scriptInterface, jsval ctor);
typedef IComponent* (*AllocFunc)(ScriptInterface& scriptInterface, JS::HandleValue ctor);
typedef void (*DeallocFunc)(IComponent*);
// ComponentTypes come in three types:
@ -70,7 +70,7 @@ private:
DeallocFunc dealloc;
std::string name;
std::string schema; // RelaxNG fragment
CScriptValRooted ctor; // only valid if type == CT_Script
DefPersistentRooted<JS::Value> ctor; // only valid if type == CT_Script
};
struct FindJSONFilesCallbackData
@ -268,24 +268,24 @@ public:
private:
// Implementations of functions exposed to scripts
static void Script_RegisterComponentType_Common(ScriptInterface::CxPrivate* pCxPrivate, int iid, std::string cname, CScriptVal ctor, bool reRegister, bool systemComponent);
static void Script_RegisterComponentType(ScriptInterface::CxPrivate* pCxPrivate, int iid, std::string cname, CScriptVal ctor);
static void Script_RegisterSystemComponentType(ScriptInterface::CxPrivate* pCxPrivate, int iid, std::string cname, CScriptVal ctor);
static void Script_ReRegisterComponentType(ScriptInterface::CxPrivate* pCxPrivate, int iid, std::string cname, CScriptVal ctor);
static void Script_RegisterComponentType_Common(ScriptInterface::CxPrivate* pCxPrivate, int iid, std::string cname, JS::HandleValue ctor, bool reRegister, bool systemComponent);
static void Script_RegisterComponentType(ScriptInterface::CxPrivate* pCxPrivate, int iid, std::string cname, JS::HandleValue ctor);
static void Script_RegisterSystemComponentType(ScriptInterface::CxPrivate* pCxPrivate, int iid, std::string cname, JS::HandleValue ctor);
static void Script_ReRegisterComponentType(ScriptInterface::CxPrivate* pCxPrivate, int iid, std::string cname, JS::HandleValue ctor);
static void Script_RegisterInterface(ScriptInterface::CxPrivate* pCxPrivate, std::string name);
static void Script_RegisterMessageType(ScriptInterface::CxPrivate* pCxPrivate, std::string name);
static void Script_RegisterGlobal(ScriptInterface::CxPrivate* pCxPrivate, std::string name, CScriptVal value);
static void Script_RegisterGlobal(ScriptInterface::CxPrivate* pCxPrivate, std::string name, JS::HandleValue value);
static IComponent* Script_QueryInterface(ScriptInterface::CxPrivate* pCxPrivate, int ent, int iid);
static std::vector<int> Script_GetEntitiesWithInterface(ScriptInterface::CxPrivate* pCxPrivate, int iid);
static std::vector<IComponent*> Script_GetComponentsWithInterface(ScriptInterface::CxPrivate* pCxPrivate, int iid);
static void Script_PostMessage(ScriptInterface::CxPrivate* pCxPrivate, int ent, int mtid, CScriptVal data);
static void Script_BroadcastMessage(ScriptInterface::CxPrivate* pCxPrivate, int mtid, CScriptVal data);
static void Script_PostMessage(ScriptInterface::CxPrivate* pCxPrivate, int ent, int mtid, JS::HandleValue data);
static void Script_BroadcastMessage(ScriptInterface::CxPrivate* pCxPrivate, int mtid, JS::HandleValue data);
static int Script_AddEntity(ScriptInterface::CxPrivate* pCxPrivate, std::string templateName);
static int Script_AddLocalEntity(ScriptInterface::CxPrivate* pCxPrivate, std::string templateName);
static void Script_DestroyEntity(ScriptInterface::CxPrivate* pCxPrivate, int ent);
static void Script_FlushDestroyedEntities(ScriptInterface::CxPrivate* pCxPrivate);
static CScriptVal Script_ReadJSONFile(ScriptInterface::CxPrivate* pCxPrivate, std::wstring fileName);
static CScriptVal Script_ReadCivJSONFile(ScriptInterface::CxPrivate* pCxPrivate, std::wstring fileName);
static JS::Value Script_ReadJSONFile(ScriptInterface::CxPrivate* pCxPrivate, std::wstring fileName);
static JS::Value Script_ReadCivJSONFile(ScriptInterface::CxPrivate* pCxPrivate, std::wstring fileName);
static std::vector<std::string> Script_FindJSONFiles(ScriptInterface::CxPrivate* pCxPrivate, std::wstring subPath, bool recursive);
static JS::Value ReadJSONFile(ScriptInterface::CxPrivate* pCxPrivate, std::wstring filePath, std::wstring fileName);

View File

@ -38,7 +38,7 @@ bool IComponent::NewJSObject(ScriptInterface& UNUSED(scriptInterface), JS::Mutab
return false;
}
jsval IComponent::GetJSInstance() const
JS::Value IComponent::GetJSInstance() const
{
return JSVAL_NULL;
return JS::NullValue();
}

Some files were not shown because too many files have changed in this diff Show More