1
0
forked from 0ad/0ad

Second (main) commit for the SpiderMonkey upgrade.

This commit contains all the required changes to our source files and
build scripts (hopefully).
A next commit will remove the old stuff of SpiderMonkey 1.8.5.

Spcial thanks to:
 - H4writer who helped a lot mainly with the performance issues we
had/have, but also with other problems or questions.
 - Leper for the review.
 - Historic_bruno for implementing the build scripts on Mac OS X and
testing on the Mac.
 - The people from the #jsapi channel and from
mozilla.dev.tech.js-engine who answered a lot of questions and helped
solving problems.
 - All the other people who helped

Refs #1886
Fixes #2442
Fixes #2416

This was SVN commit r14877.
This commit is contained in:
Yves 2014-03-28 20:26:32 +00:00
parent 6f6b841af3
commit e9e05f4efc
58 changed files with 1661 additions and 1378 deletions

View File

@ -562,38 +562,42 @@ extern_lib_defs = {
},
spidermonkey = {
compile_settings = function()
if _OPTIONS["with-system-mozjs185"] then
if _OPTIONS["with-system-mozjs24"] then
if not _OPTIONS["android"] then
pkgconfig_cflags("mozjs185")
pkgconfig_cflags("mozjs-24")
end
defines { "WITH_SYSTEM_MOZJS185" }
defines { "WITH_SYSTEM_MOZJS24" }
else
if os.is("windows") then
include_dir = "include-win32"
elseif os.is("macosx") then
include_dir = "include"
else
include_dir = "include-unix"
end
configuration "Debug"
includedirs { libraries_source_dir.."spidermonkey/"..include_dir }
includedirs { libraries_source_dir.."spidermonkey/"..include_dir.."-debug" }
defines { "DEBUG" }
configuration "Release"
includedirs { libraries_source_dir.."spidermonkey/"..include_dir }
includedirs { libraries_source_dir.."spidermonkey/"..include_dir.."-release" }
configuration { }
end
end,
link_settings = function()
if _OPTIONS["with-system-mozjs185"] then
if _OPTIONS["with-system-mozjs24"] then
if _OPTIONS["android"] then
links { "mozjs185-1.0" }
links { "mozjs-24" }
else
pkgconfig_libs("mozjs185")
pkgconfig_libs("nspr")
pkgconfig_libs("mozjs-24")
end
else
if os.is("macosx") then
add_default_lib_paths("nspr")
links { "nspr4", "plc4", "plds4" }
end
configuration "Debug"
links { "mozjs185-ps-debug" }
links { "mozjs24-ps-debug" }
configuration "Release"
links { "mozjs185-ps-release" }
links { "mozjs24-ps-release" }
configuration { }
add_source_lib_paths("spidermonkey")
end

View File

@ -15,7 +15,7 @@ newoption { trigger = "without-miniupnpc", description = "Disable use of miniupn
newoption { trigger = "with-system-nvtt", description = "Search standard paths for nvidia-texture-tools library, instead of using bundled copy" }
newoption { trigger = "with-system-enet", description = "Search standard paths for libenet, instead of using bundled copy" }
newoption { trigger = "with-system-miniupnpc", description = "Search standard paths for libminiupnpc, instead of using bundled copy" }
newoption { trigger = "with-system-mozjs185", description = "Search standard paths for libmozjs185, instead of using bundled copy" }
newoption { trigger = "with-system-mozjs24", description = "Search standard paths for libmozjs24, instead of using bundled copy" }
newoption { trigger = "with-c++11", description = "Enable C++11 on GCC" }
newoption { trigger = "sysroot", description = "Set compiler system root path, used for building against a non-system SDK. For example /usr/local becomes SYSROOT/user/local" }
newoption { trigger = "macosx-version-min", description = "Set minimum required version of the OS X API, the build will possibly fail if an older SDK is used, while newer API functions will be weakly linked (i.e. resolved at runtime)" }

View File

@ -37,7 +37,7 @@ without_nvtt=false
with_system_nvtt=false
with_system_enet=false
with_system_miniupnpc=false
with_system_mozjs185=false
with_system_mozjs24=false
enable_atlas=true
for i in "$@"
@ -47,7 +47,7 @@ do
--with-system-nvtt ) with_system_nvtt=true; premake_args="${premake_args} --with-system-nvtt" ;;
--with-system-enet ) with_system_enet=true; premake_args="${premake_args} --with-system-enet" ;;
--with-system-miniupnpc ) with_system_miniupnpc=true; premake_args="${premake_args} --with-system-miniupnpc" ;;
--with-system-mozjs185 ) with_system_mozjs185=true; premake_args="${premake_args} --with-system-mozjs185" ;;
--with-system-mozjs24 ) with_system_mozjs24=true; premake_args="${premake_args} --with-system-mozjs24" ;;
--enable-atlas ) enable_atlas=true ;;
--disable-atlas ) enable_atlas=false ;;
-j* ) JOBS=$i ;;
@ -81,7 +81,7 @@ if [ "`uname -s`" != "Darwin" ]; then
# Build/update bundled external libraries
(cd ../../libraries/source/fcollada/src && ${MAKE} ${JOBS}) || die "FCollada build failed"
echo
if [ "$with_system_mozjs185" = "false" ]; then
if [ "$with_system_mozjs24" = "false" ]; then
(cd ../../libraries/source/spidermonkey && MAKE=${MAKE} JOBS=${JOBS} ./build.sh) || die "SpiderMonkey build failed"
fi
echo

View File

@ -38,9 +38,11 @@ OGG_VERSION="libogg-1.3.0"
VORBIS_VERSION="libvorbis-1.3.3"
# gloox is necessary for multiplayer lobby
GLOOX_VERSION="gloox-1.0.9"
# NSPR is necessary for threadsafe Spidermonkey
NSPR_VERSION="4.10.3"
# --------------------------------------------------------------
# Bundled with the game:
# * SpiderMonkey 1.8.5
# * SpiderMonkey 24
# * ENet 1.3.3
# * NVTT
# * FCollada
@ -520,32 +522,69 @@ else
fi
popd > /dev/null
# --------------------------------------------------------------
echo -e "Building NSPR..."
LIB_VERSION="${NSPR_VERSION}"
LIB_ARCHIVE="nspr-$LIB_VERSION.tar.gz"
LIB_DIRECTORY="nspr-$LIB_VERSION"
LIB_URL="https://ftp.mozilla.org/pub/mozilla.org/nspr/releases/v$LIB_VERSION/src/"
mkdir -p nspr
pushd nspr > /dev/null
NSPR_DIR="$(pwd)"
if [[ "$force_rebuild" = "true" ]] || [[ ! -e .already-built ]] || [[ .already-built -ot $LIB_DIRECTORY ]]
then
rm -f .already-built
download_lib $LIB_URL $LIB_ARCHIVE
rm -rf $LIB_DIRECTORY bin include lib share
tar -xf $LIB_ARCHIVE
pushd $LIB_DIRECTORY/nspr
(CFLAGS="$CFLAGS" CPPFLAGS="$CPPFLAGS" LDFLAGS="$LDFLAGS" ./configure --prefix="$NSPR_DIR" && make ${JOBS} && make install) || die "NSPR build failed"
popd
# TODO: how can we not build the dylibs?
rm -f lib/*.dylib
touch .already-built
else
already_built
fi
popd > /dev/null
# --------------------------------------------------------------------
# The following libraries are shared on different OSes and may
# be customzied, so we build and install them from bundled sources
# --------------------------------------------------------------------
echo -e "Building Spidermonkey..."
LIB_VERSION="js185-1.0.0"
LIB_ARCHIVE="$LIB_VERSION.tar.gz"
LIB_DIRECTORY="js-1.8.5"
LIB_VERSION="mozjs-24.2.0"
LIB_ARCHIVE="$LIB_VERSION.tar.bz2"
LIB_DIRECTORY="mozjs24"
pushd ../source/spidermonkey/ > /dev/null
if [[ "$force_rebuild" = "true" ]] || [[ ! -e .already-built ]] || [[ .already-built -ot $LIB_DIRECTORY ]]
then
INSTALL_DIR="$(pwd)"
INCLUDE_DIR_DEBUG=$INSTALL_DIR/include-unix-debug
INCLUDE_DIR_RELEASE=$INSTALL_DIR/include-unix-release
rm -f .already-built
rm -f lib/*.a
rm -rf $LIB_DIRECTORY
rm -rf $LIB_DIRECTORY $INCLUDE_DIR_DEBUG $INCLUDE_DIR_RELEASE
tar -xf $LIB_ARCHIVE
# rename the extracted directory to something shorter
mv $LIB_VERSION $LIB_DIRECTORY
pushd $LIB_DIRECTORY/js/src
# We want separate debug/release versions of the library, so change their install name in the Makefile
sed -i.bak -e 's/\(STATIC_LIBRARY_NAME),mozjs185-\)\(\$(SRCREL_ABI_VERSION)\)\{0,1\}/\1ps-debug/' Makefile.in
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
CONF_OPTS="--prefix=${INSTALL_DIR} --disable-tests --disable-shared-js"
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"
# 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
@ -555,19 +594,32 @@ 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="$CC -arch $ARCH" CXX="$CXX -arch $ARCH" AR=ar CROSS_COMPILE=1 ../configure $CONF_OPTS --enable-debug --disable-optimize && make ${JOBS} && make install) || die "Spidermonkey build failed"
(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"
# js-config.h is different for debug and release builds, so we need different include directories for both
mkdir -p $INCLUDE_DIR_DEBUG
cp -R -L dist/include/* $INCLUDE_DIR_DEBUG/
cp *.a $INSTALL_DIR/lib
popd
mv Makefile.in.bak Makefile.in
sed -i.bak -e 's/\(STATIC_LIBRARY_NAME),mozjs185-\)\(\$(SRCREL_ABI_VERSION)\)\{0,1\}/\1ps-release/' 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
mkdir -p build-release
pushd build-release
(CC="$CC -arch $ARCH" CXX="$CXX -arch $ARCH" AR=ar CROSS_COMPILE=1 ../configure $CONF_OPTS && make ${JOBS} && make install) || die "Spidermonkey build failed"
(CC="clang" CXX="clang++" AR=ar CROSS_COMPILE=1 ../configure $CONF_OPTS --enable-optimize && make ${JOBS}) || die "Spidermonkey build failed"
# js-config.h is different for debug and release builds, so we need different include directories for both
mkdir -p $INCLUDE_DIR_RELEASE
cp -R -L dist/include/* $INCLUDE_DIR_RELEASE/
cp *.a $INSTALL_DIR/lib
popd
mv Makefile.in.bak Makefile.in
mv shell/Makefile.in.bak shell/Makefile.in
popd
touch .already-built

View File

@ -0,0 +1,16 @@
--- 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

@ -30,7 +30,7 @@ class CCinemaManager;
class CVector3D;
struct SViewPort;
struct JSObject;
class JSObject;
class CGameViewImpl;

View File

@ -37,13 +37,6 @@ CMapGeneratorWorker::~CMapGeneratorWorker()
{
// Wait for thread to end
pthread_join(m_WorkerThread, NULL);
// The StructuredClone destructor references a JSContext created by our
// ScriptInterface, so explicitly clean it up before ScriptInterface
m_MapData.reset();
// Cleanup ScriptInterface
delete m_ScriptInterface;
}
void CMapGeneratorWorker::Initialize(const VfsPath& scriptFile, const std::string& settings)
@ -99,40 +92,52 @@ bool CMapGeneratorWorker::Run()
m_ScriptInterface->RegisterFunction<void, CMapGeneratorWorker::MaybeGC>("MaybeGC");
m_ScriptInterface->RegisterFunction<std::vector<std::string>, CMapGeneratorWorker::GetCivData>("GetCivData");
// Parse settings
CScriptValRooted settingsVal = m_ScriptInterface->ParseJSON(m_Settings);
if (settingsVal.undefined())
// TODO: This code is a bit ugly because we have to ensure that CScriptValRooted gets destroyed before the ScriptInterface.
// In the future we should work more with the standard JSAPI types for rooting on the stack, which should avoid such problems.
bool ret = true;
{
LOGERROR(L"CMapGeneratorWorker::Run: Failed to parse settings");
return false;
// Parse settings
CScriptValRooted settingsVal = m_ScriptInterface->ParseJSON(m_Settings);
if (settingsVal.undefined())
{
LOGERROR(L"CMapGeneratorWorker::Run: Failed to parse settings");
ret = false;
}
else
{
// Init RNG seed
u32 seed;
if (!m_ScriptInterface->GetProperty(settingsVal.get(), "Seed", seed))
{ // No seed specified
LOGWARNING(L"CMapGeneratorWorker::Run: No seed value specified - using 0");
seed = 0;
}
m_MapGenRNG.seed(seed);
// Copy settings to global variable
if (!m_ScriptInterface->SetProperty(m_ScriptInterface->GetGlobalObject(), "g_MapSettings", settingsVal))
{
LOGERROR(L"CMapGeneratorWorker::Run: Failed to define g_MapSettings");
ret = false;
}
else
{
// Load RMS
LOGMESSAGE(L"Loading RMS '%ls'", m_ScriptPath.string().c_str());
if (!m_ScriptInterface->LoadGlobalScriptFile(m_ScriptPath))
{
LOGERROR(L"CMapGeneratorWorker::Run: Failed to load RMS '%ls'", m_ScriptPath.string().c_str());
ret = false;
}
}
}
}
// We must destroy the ScriptInterface in the same thread because the JSAPI requires that!
SAFE_DELETE(m_ScriptInterface);
// Init RNG seed
uint32_t seed;
if (!m_ScriptInterface->GetProperty(settingsVal.get(), "Seed", seed))
{ // No seed specified
LOGWARNING(L"CMapGeneratorWorker::Run: No seed value specified - using 0");
seed = 0;
}
m_MapGenRNG.seed((int32_t)seed);
// Copy settings to global variable
if (!m_ScriptInterface->SetProperty(m_ScriptInterface->GetGlobalObject(), "g_MapSettings", settingsVal))
{
LOGERROR(L"CMapGeneratorWorker::Run: Failed to define g_MapSettings");
return false;
}
// Load RMS
LOGMESSAGE(L"Loading RMS '%ls'", m_ScriptPath.string().c_str());
if (!m_ScriptInterface->LoadGlobalScriptFile(m_ScriptPath))
{
LOGERROR(L"CMapGeneratorWorker::Run: Failed to load RMS '%ls'", m_ScriptPath.string().c_str());
return false;
}
return true;
return ret;
}
int CMapGeneratorWorker::GetProgress()

View File

@ -77,7 +77,7 @@ struct SGUIStyle
std::map<CStr, CStrW> m_SettingsDefaults;
};
struct JSObject; // The GUI stores a JSObject*, so needs to know that JSObject exists
class JSObject; // The GUI stores a JSObject*, so needs to know that JSObject exists
class IGUIObject;
class CGUISpriteInstance;
struct SGUIText;

View File

@ -71,24 +71,7 @@ void CGUIManager::SwitchPage(const CStrW& pageName, ScriptInterface* srcScriptIn
shared_ptr<ScriptInterface::StructuredClone> initDataClone;
if (initData.get() != JSVAL_VOID)
{
ENSURE(srcScriptInterface);
// TODO: Fix this horribly ugly hack as soon as we have integrated the new SpiderMonkey library version.
// In SpiderMonkey 1.8.5, StructuredClone data needs to be freed with JS_Free.
// JS_Free takes a JSContext as argument which must be the one the clone was created wtih.
// This means we must ensure to call the destructor of all structuredclones before the context is destroyed.
// To achieve this, we write all initData structured clones with the GUIManager's JSContext.
// That's why we have to clone the value twice here.
// Calling WriteStructuredClone with a context in another compartment than the value works, but I don't think that's
// supposed to work and will probably break under some conditions.
// Newer SpiderMonkey versions introduce JS_ClearStructuredClone instead of JS_Free which does not require a context.
if (srcScriptInterface != m_ScriptInterface.get())
{
CScriptVal cloneSaveInitData;
cloneSaveInitData = m_ScriptInterface->CloneValueFromOtherContext(*srcScriptInterface, initData.get());
initDataClone = m_ScriptInterface->WriteStructuredClone(cloneSaveInitData.get());
}
else
initDataClone = m_ScriptInterface->WriteStructuredClone(initData.get());
initDataClone = srcScriptInterface->WriteStructuredClone(initData.get());
}
m_PageStack.clear();
PushPage(pageName, initDataClone);

View File

@ -30,7 +30,7 @@
#include <set>
class CGUI;
struct JSObject;
class JSObject;
class IGUIObject;
struct CColor;
struct SGUIIcon;

View File

@ -422,6 +422,7 @@ void IGUIObject::RegisterScriptHandler(const CStr& Action, const CStr& Code, CGU
throw PSERROR_GUI_OperationNeedsGUIObject();
JSContext* cx = pGUI->GetScriptInterface()->GetContext();
JSAutoRequest rq(cx);
const int paramCount = 1;
const char* paramNames[paramCount] = { "mouse" };
@ -445,7 +446,7 @@ void IGUIObject::RegisterScriptHandler(const CStr& Action, const CStr& Code, CGU
void IGUIObject::SetScriptHandler(const CStr& Action, JSObject* Function)
{
m_ScriptHandlers[Action] = CScriptValRooted(m_pGUI->GetScriptInterface()->GetContext(), OBJECT_TO_JSVAL(Function));
m_ScriptHandlers[Action] = CScriptValRooted(m_pGUI->GetScriptInterface()->GetContext(), JS::ObjectValue(*Function));
}
InReaction IGUIObject::SendEvent(EGUIMessageType type, const CStr& EventName)
@ -469,6 +470,7 @@ void IGUIObject::ScriptEvent(const CStr& Action)
return;
JSContext* cx = m_pGUI->GetScriptInterface()->GetContext();
JSAutoRequest rq(cx);
// Set up the 'mouse' parameter
CScriptVal mouse;
@ -480,7 +482,7 @@ void IGUIObject::ScriptEvent(const CStr& Action)
jsval paramData[] = { mouse.get() };
jsval result;
JSBool ok = JS_CallFunctionValue(cx, GetJSObject(), (*it).second.get(), ARRAY_SIZE(paramData), paramData, &result);
bool ok = JS_CallFunctionValue(cx, GetJSObject(), (*it).second.get(), ARRAY_SIZE(paramData), paramData, &result);
if (!ok)
{
// We have no way to propagate the script exception, so just ignore it
@ -490,17 +492,19 @@ void IGUIObject::ScriptEvent(const CStr& Action)
void IGUIObject::ScriptEvent(const CStr& Action, const CScriptValRooted& Argument)
{
JSContext* cx = m_pGUI->GetScriptInterface()->GetContext();
std::map<CStr, CScriptValRooted>::iterator it = m_ScriptHandlers.find(Action);
if (it == m_ScriptHandlers.end())
return;
JSContext* cx = m_pGUI->GetScriptInterface()->GetContext();
JSAutoRequest rq(cx);
JSObject* object = GetJSObject();
jsval arg = Argument.get();
jsval result;
JSBool ok = JS_CallFunctionValue(cx, object, (*it).second.get(), 1, &arg, &result);
bool ok = JS_CallFunctionValue(cx, object, (*it).second.get(), 1, &arg, &result);
if (!ok)
{
JS_ReportError(cx, "Errors executing script action \"%s\"", Action.c_str());
@ -510,6 +514,7 @@ void IGUIObject::ScriptEvent(const CStr& Action, const CScriptValRooted& Argumen
JSObject* IGUIObject::GetJSObject()
{
JSContext* cx = m_pGUI->GetScriptInterface()->GetContext();
JSAutoRequest rq(cx);
// 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.
@ -517,7 +522,7 @@ JSObject* IGUIObject::GetJSObject()
{
JSObject* obj = JS_NewObject(cx, &JSI_IGUIObject::JSI_class, NULL, NULL);
m_JSObject = CScriptValRooted(cx, OBJECT_TO_JSVAL(obj));
JS_SetPrivate(cx, JSVAL_TO_OBJECT(m_JSObject.get()), this);
JS_SetPrivate(JSVAL_TO_OBJECT(m_JSObject.get()), this);
}
return JSVAL_TO_OBJECT(m_JSObject.get());;
}

View File

@ -69,7 +69,7 @@ class CScriptValRooted;
// Types
//--------------------------------------------------------
struct JSObject;
class JSObject;
//--------------------------------------------------------
// Error declarations
@ -142,9 +142,9 @@ class IGUIObject
friend class GUITooltip;
// Allow getProperty to access things like GetParent()
friend JSBool JSI_IGUIObject::getProperty(JSContext* cx, JSObject* obj, jsid id, jsval* vp);
friend JSBool JSI_IGUIObject::setProperty(JSContext* cx, JSObject* obj, jsid id, JSBool strict, jsval* vp);
friend JSBool JSI_IGUIObject::getComputedSize(JSContext* cx, uintN argc, jsval* vp);
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);
public:
IGUIObject();

View File

@ -23,14 +23,13 @@
#include "lib/external_libraries/libsdl.h"
#include "ps/Hotkey.h"
#include "js/jsapi.h"
#define SET(obj, name, value) STMT(jsval v_ = ToJSVal(cx, (value)); JS_SetProperty(cx, (obj), (name), &v_))
#define SET(obj, name, value) STMT(JS::RootedValue v_(cx); ToJSVal(cx, v_.get(), (value)); JS_SetProperty(cx, (obj), (name), v_.address()))
// ignore JS_SetProperty return value, because errors should be impossible
// and we can't do anything useful in the case of errors anyway
template<> jsval ScriptInterface::ToJSVal<SDL_Event_>(JSContext* cx, SDL_Event_ const& val)
template<> void ScriptInterface::ToJSVal<SDL_Event_>(JSContext* cx, JS::Value& ret, SDL_Event_ const& val)
{
JSAutoRequest rq(cx);
const char* typeName;
switch (val.ev.type)
@ -55,7 +54,10 @@ template<> jsval ScriptInterface::ToJSVal<SDL_Event_>(JSContext* cx, SDL_Event_
JSObject* obj = JS_NewObject(cx, NULL, NULL, NULL);
if (! obj)
return JSVAL_VOID;
{
ret = JSVAL_VOID;
return;
}
SET(obj, "type", typeName);
@ -77,9 +79,12 @@ template<> jsval ScriptInterface::ToJSVal<SDL_Event_>(JSContext* cx, SDL_Event_
JSObject* keysym = JS_NewObject(cx, NULL, NULL, NULL);
if (! keysym)
return JSVAL_VOID;
jsval keysymVal = OBJECT_TO_JSVAL(keysym);
JS_SetProperty(cx, obj, "keysym", &keysymVal);
{
ret = JSVAL_VOID;
return;
}
JS::RootedValue keysymVal(cx, JS::ObjectValue(*keysym));
JS_SetProperty(cx, obj, "keysym", keysymVal.address());
// SET(keysym, "scancode", (int)val.ev.key.keysym.scancode); // (not in wsdl.h)
SET(keysym, "sym", (int)val.ev.key.keysym.sym);
@ -126,15 +131,13 @@ template<> jsval ScriptInterface::ToJSVal<SDL_Event_>(JSContext* cx, SDL_Event_
}
}
jsval rval = OBJECT_TO_JSVAL(obj);
return rval;
ret = JS::ObjectValue(*obj);
}
template<> jsval ScriptInterface::ToJSVal<IGUIObject*>(JSContext* UNUSED(cx), IGUIObject* const& val)
template<> void ScriptInterface::ToJSVal<IGUIObject*>(JSContext* UNUSED(cx), JS::Value& ret, IGUIObject* const& val)
{
if (val == NULL)
return JSVAL_NULL;
return OBJECT_TO_JSVAL(val->GetJSObject());
ret = JSVAL_NULL;
else
ret = JS::ObjectValue(*val->GetJSObject());
}

View File

@ -23,10 +23,10 @@
/**** GUISize ****/
JSClass JSI_GUISize::JSI_class = {
"GUISize", 0,
JS_PropertyStub, JS_PropertyStub,
JS_PropertyStub, JS_DeletePropertyStub,
JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, JS_ResolveStub,
JS_ConvertStub, JS_FinalizeStub,
JS_ConvertStub, NULL,
NULL, NULL, NULL, JSI_GUISize::construct
};
@ -45,48 +45,60 @@ JSPropertySpec JSI_GUISize::JSI_props[] =
JSFunctionSpec JSI_GUISize::JSI_methods[] =
{
{ "toString", JSI_GUISize::toString, 0, 0 },
{ 0 }
JS_FS("toString", JSI_GUISize::toString, 0, 0),
JS_FS_END
};
JSBool JSI_GUISize::construct(JSContext* cx, uintN argc, jsval* vp)
JSBool JSI_GUISize::construct(JSContext* cx, uint argc, jsval* vp)
{
JSObject* obj = JS_NewObject(cx, &JSI_GUISize::JSI_class, NULL, NULL);
if (argc == 8)
{
JS_SetProperty(cx, obj, "left", &JS_ARGV(cx, vp)[0]);
JS_SetProperty(cx, obj, "top", &JS_ARGV(cx, vp)[1]);
JS_SetProperty(cx, obj, "right", &JS_ARGV(cx, vp)[2]);
JS_SetProperty(cx, obj, "bottom", &JS_ARGV(cx, vp)[3]);
JS_SetProperty(cx, obj, "rleft", &JS_ARGV(cx, vp)[4]);
JS_SetProperty(cx, obj, "rtop", &JS_ARGV(cx, vp)[5]);
JS_SetProperty(cx, obj, "rright", &JS_ARGV(cx, vp)[6]);
JS_SetProperty(cx, obj, "rbottom", &JS_ARGV(cx, vp)[7]);
JS::RootedValue v0(cx, JS_ARGV(cx, vp)[0]);
JS::RootedValue v1(cx, JS_ARGV(cx, vp)[1]);
JS::RootedValue v2(cx, JS_ARGV(cx, vp)[2]);
JS::RootedValue v3(cx, JS_ARGV(cx, vp)[3]);
JS::RootedValue v4(cx, JS_ARGV(cx, vp)[4]);
JS::RootedValue v5(cx, JS_ARGV(cx, vp)[5]);
JS::RootedValue v6(cx, JS_ARGV(cx, vp)[6]);
JS::RootedValue v7(cx, JS_ARGV(cx, vp)[7]);
JS_SetProperty(cx, obj, "left", v0.address());
JS_SetProperty(cx, obj, "top", v1.address());
JS_SetProperty(cx, obj, "right", v2.address());
JS_SetProperty(cx, obj, "bottom", v3.address());
JS_SetProperty(cx, obj, "rleft", v4.address());
JS_SetProperty(cx, obj, "rtop", v5.address());
JS_SetProperty(cx, obj, "rright", v6.address());
JS_SetProperty(cx, obj, "rbottom", v7.address());
}
else if (argc == 4)
{
jsval zero = JSVAL_ZERO;
JS_SetProperty(cx, obj, "left", &JS_ARGV(cx, vp)[0]);
JS_SetProperty(cx, obj, "top", &JS_ARGV(cx, vp)[1]);
JS_SetProperty(cx, obj, "right", &JS_ARGV(cx, vp)[2]);
JS_SetProperty(cx, obj, "bottom", &JS_ARGV(cx, vp)[3]);
JS_SetProperty(cx, obj, "rleft", &zero);
JS_SetProperty(cx, obj, "rtop", &zero);
JS_SetProperty(cx, obj, "rright", &zero);
JS_SetProperty(cx, obj, "rbottom", &zero);
JS::RootedValue zero(cx, JSVAL_ZERO);
JS::RootedValue v0(cx, JS_ARGV(cx, vp)[0]);
JS::RootedValue v1(cx, JS_ARGV(cx, vp)[1]);
JS::RootedValue v2(cx, JS_ARGV(cx, vp)[2]);
JS::RootedValue v3(cx, JS_ARGV(cx, vp)[3]);
JS_SetProperty(cx, obj, "left", v0.address());
JS_SetProperty(cx, obj, "top", v1.address());
JS_SetProperty(cx, obj, "right", v2.address());
JS_SetProperty(cx, obj, "bottom", v3.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());
}
else
{
jsval zero = JSVAL_ZERO;
JS_SetProperty(cx, obj, "left", &zero);
JS_SetProperty(cx, obj, "top", &zero);
JS_SetProperty(cx, obj, "right", &zero);
JS_SetProperty(cx, obj, "bottom", &zero);
JS_SetProperty(cx, obj, "rleft", &zero);
JS_SetProperty(cx, obj, "rtop", &zero);
JS_SetProperty(cx, obj, "rright", &zero);
JS_SetProperty(cx, obj, "rbottom", &zero);
JS::RootedValue zero(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_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(obj));
@ -102,7 +114,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, uintN argc, jsval* vp)
JSBool JSI_GUISize::toString(JSContext* cx, uint argc, jsval* vp)
{
UNUSED2(argc);
@ -141,10 +153,10 @@ JSBool JSI_GUISize::toString(JSContext* cx, uintN argc, jsval* vp)
JSClass JSI_GUIColor::JSI_class = {
"GUIColor", 0,
JS_PropertyStub, JS_PropertyStub,
JS_PropertyStub, JS_DeletePropertyStub,
JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, JS_ResolveStub,
JS_ConvertStub, JS_FinalizeStub,
JS_ConvertStub, NULL,
NULL, NULL, NULL, JSI_GUIColor::construct
};
@ -159,11 +171,11 @@ JSPropertySpec JSI_GUIColor::JSI_props[] =
JSFunctionSpec JSI_GUIColor::JSI_methods[] =
{
{ "toString", JSI_GUIColor::toString, 0, 0 },
{ 0 }
JS_FS("toString", JSI_GUIColor::toString, 0, 0),
JS_FS_END
};
JSBool JSI_GUIColor::construct(JSContext* cx, uintN argc, jsval* vp)
JSBool JSI_GUIColor::construct(JSContext* cx, uint argc, jsval* vp)
{
JSObject* obj = JS_NewObject(cx, &JSI_GUIColor::JSI_class, NULL, NULL);
@ -177,22 +189,19 @@ JSBool JSI_GUIColor::construct(JSContext* cx, uintN argc, jsval* vp)
else
{
// Nice magenta:
jsval c;
if (!JS_NewNumberValue(cx, 1.0, &c))
return JS_FALSE;
JS_SetProperty(cx, obj, "r", &c);
JS_SetProperty(cx, obj, "b", &c);
JS_SetProperty(cx, obj, "a", &c);
if (!JS_NewNumberValue(cx, 0.0, &c))
return JS_FALSE;
JS_SetProperty(cx, obj, "g", &c);
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());
c = JS::NumberValue(0.0);
JS_SetProperty(cx, obj, "g", c.address());
}
JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(obj));
return JS_TRUE;
}
JSBool JSI_GUIColor::toString(JSContext* cx, uintN argc, jsval* vp)
JSBool JSI_GUIColor::toString(JSContext* cx, uint argc, jsval* vp)
{
UNUSED2(argc);
@ -218,10 +227,10 @@ JSBool JSI_GUIColor::toString(JSContext* cx, uintN argc, jsval* vp)
JSClass JSI_GUIMouse::JSI_class = {
"GUIMouse", 0,
JS_PropertyStub, JS_PropertyStub,
JS_PropertyStub, JS_DeletePropertyStub,
JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, JS_ResolveStub,
JS_ConvertStub, JS_FinalizeStub,
JS_ConvertStub, NULL,
NULL, NULL, NULL, JSI_GUIMouse::construct
};
@ -235,37 +244,40 @@ JSPropertySpec JSI_GUIMouse::JSI_props[] =
JSFunctionSpec JSI_GUIMouse::JSI_methods[] =
{
{ "toString", JSI_GUIMouse::toString, 0, 0 },
{ 0 }
JS_FS("toString", JSI_GUIMouse::toString, 0, 0),
JS_FS_END
};
JSBool JSI_GUIMouse::construct(JSContext* cx, uintN argc, jsval* vp)
JSBool JSI_GUIMouse::construct(JSContext* cx, uint argc, jsval* vp)
{
JSObject* obj = JS_NewObject(cx, &JSI_GUIMouse::JSI_class, NULL, NULL);
if (argc == 3)
{
JS_SetProperty(cx, obj, "x", &JS_ARGV(cx, vp)[0]);
JS_SetProperty(cx, obj, "y", &JS_ARGV(cx, vp)[1]);
JS_SetProperty(cx, obj, "buttons", &JS_ARGV(cx, vp)[2]);
JS::RootedValue v0(cx, JS_ARGV(cx, vp)[0]);
JS::RootedValue v1(cx, JS_ARGV(cx, vp)[1]);
JS::RootedValue v2(cx, JS_ARGV(cx, vp)[2]);
JS_SetProperty(cx, obj, "x", v0.address());
JS_SetProperty(cx, obj, "y", v1.address());
JS_SetProperty(cx, obj, "buttons", v2.address());
}
else
{
jsval zero = JSVAL_ZERO;
JS_SetProperty(cx, obj, "x", &zero);
JS_SetProperty(cx, obj, "y", &zero);
JS_SetProperty(cx, obj, "buttons", &zero);
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_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(obj));
return JS_TRUE;
}
JSBool JSI_GUIMouse::toString(JSContext* cx, uintN argc, jsval* vp)
JSBool JSI_GUIMouse::toString(JSContext* cx, uint argc, jsval* vp)
{
UNUSED2(argc);
int32 x, y, buttons;
i32 x, y, buttons;
ScriptInterface* pScriptInterface = ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface;
pScriptInterface->GetProperty(JS_THIS_VALUE(cx, vp), "x", x);
pScriptInterface->GetProperty(JS_THIS_VALUE(cx, vp), "y", y);

View File

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

View File

@ -32,28 +32,28 @@
JSClass JSI_IGUIObject::JSI_class = {
"GUIObject", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub,
JS_PropertyStub, JS_DeletePropertyStub,
JSI_IGUIObject::getProperty, JSI_IGUIObject::setProperty,
JS_EnumerateStub, JS_ResolveStub,
JS_ConvertStub, JS_FinalizeStub,
JS_ConvertStub, NULL,
NULL, NULL, NULL, JSI_IGUIObject::construct
};
JSPropertySpec JSI_IGUIObject::JSI_props[] =
JSPropertySpec JSI_IGUIObject::JSI_props[] =
{
{ 0 }
};
JSFunctionSpec JSI_IGUIObject::JSI_methods[] =
JSFunctionSpec JSI_IGUIObject::JSI_methods[] =
{
{ "toString", JSI_IGUIObject::toString, 0, 0 },
{ "focus", JSI_IGUIObject::focus, 0, 0 },
{ "blur", JSI_IGUIObject::blur, 0, 0 },
{ "getComputedSize", JSI_IGUIObject::getComputedSize, 0, 0 },
{ 0 }
JS_FS("toString", JSI_IGUIObject::toString, 0, 0),
JS_FS("focus", JSI_IGUIObject::focus, 0, 0),
JS_FS("blur", JSI_IGUIObject::blur, 0, 0),
JS_FS("getComputedSize", JSI_IGUIObject::getComputedSize, 0, 0),
JS_FS_END
};
JSBool JSI_IGUIObject::getProperty(JSContext* cx, JSObject* obj, jsid id, jsval* vp)
JSBool JSI_IGUIObject::getProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::MutableHandleValue vp)
{
IGUIObject* e = (IGUIObject*)JS_GetInstancePrivate(cx, obj, &JSI_IGUIObject::JSI_class, NULL);
if (!e)
@ -88,9 +88,9 @@ JSBool JSI_IGUIObject::getProperty(JSContext* cx, JSObject* obj, jsid id, jsval*
CStr eventName (CStr(propName.substr(2)).LowerCase());
std::map<CStr, CScriptValRooted>::iterator it = e->m_ScriptHandlers.find(eventName);
if (it == e->m_ScriptHandlers.end())
*vp = JSVAL_NULL;
vp.set(JS::NullValue());
else
*vp = it->second.get();
vp.set((*it).second.get());
return JS_TRUE;
}
@ -102,19 +102,19 @@ JSBool JSI_IGUIObject::getProperty(JSContext* cx, JSObject* obj, jsid id, jsval*
if (parent)
{
// If the object isn't parentless, return a new object
*vp = OBJECT_TO_JSVAL(parent->GetJSObject());
vp.set(JS::ObjectValue(*parent->GetJSObject()));
}
else
{
// Return null if there's no parent
*vp = JSVAL_NULL;
vp.set(JS::NullValue());
}
return JS_TRUE;
}
// Also handle "name" specially
else if (propName == "name")
{
*vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, e->GetName().c_str()));
vp.set(JS::StringValue(JS_NewStringCopyZ(cx, e->GetName().c_str())));
return JS_TRUE;
}
// Handle all other properties
@ -135,7 +135,7 @@ JSBool JSI_IGUIObject::getProperty(JSContext* cx, JSObject* obj, jsid id, jsval*
{
bool value;
GUI<bool>::GetSetting(e, propName, value);
*vp = value ? JSVAL_TRUE : JSVAL_FALSE;
vp.set(JS::BooleanValue(value));
break;
}
@ -143,7 +143,7 @@ JSBool JSI_IGUIObject::getProperty(JSContext* cx, JSObject* obj, jsid id, jsval*
{
int value;
GUI<int>::GetSetting(e, propName, value);
*vp = INT_TO_JSVAL(value);
vp.set(JS::Int32Value(value));
break;
}
@ -152,19 +152,22 @@ JSBool JSI_IGUIObject::getProperty(JSContext* cx, JSObject* obj, jsid id, jsval*
float value;
GUI<float>::GetSetting(e, propName, value);
// Create a garbage-collectable double
return JS_NewNumberValue(cx, value, vp);
vp.set(JS::NumberValue(value));
return !vp.isNull();
}
case GUIST_CColor:
{
CColor colour;
GUI<CColor>::GetSetting(e, propName, colour);
JSObject* obj = JS_NewObject(cx, &JSI_GUIColor::JSI_class, NULL, NULL);
*vp = OBJECT_TO_JSVAL(obj); // root it
jsval c;
JS::RootedObject obj(cx, JS_NewObject(cx, &JSI_GUIColor::JSI_class, NULL, NULL));
vp.set(JS::ObjectValue(*obj));
JS::RootedValue c(cx);
// Attempt to minimise ugliness through macrosity
#define P(x) if (!JS_NewNumberValue(cx, colour.x, &c)) return JS_FALSE; JS_SetProperty(cx, obj, #x, &c)
#define P(x) c = JS::NumberValue(colour.x); \
if (c.isNull()) \
return false; \
JS_SetProperty(cx, obj, #x, c.address())
P(r);
P(g);
P(b);
@ -178,12 +181,12 @@ JSBool JSI_IGUIObject::getProperty(JSContext* cx, JSObject* obj, jsid id, jsval*
{
CClientArea area;
GUI<CClientArea>::GetSetting(e, propName, area);
JSObject* obj = JS_NewObject(cx, &JSI_GUISize::JSI_class, NULL, NULL);
*vp = OBJECT_TO_JSVAL(obj); // root it
vp.set(JS::ObjectValue(*JS_NewObject(cx, &JSI_GUISize::JSI_class, NULL, NULL)));
try
{
ScriptInterface* pScriptInterface = ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface;
#define P(x, y, z) pScriptInterface->SetProperty(OBJECT_TO_JSVAL(obj), #z, area.x.y, false, true)
#define P(x, y, z) pScriptInterface->SetProperty(vp, #z, area.x.y, false, true)
P(pixel, left, left);
P(pixel, top, top);
P(pixel, right, right);
@ -207,7 +210,7 @@ JSBool JSI_IGUIObject::getProperty(JSContext* cx, JSObject* obj, jsid id, jsval*
{
CGUIString value;
GUI<CGUIString>::GetSetting(e, propName, value);
*vp = ScriptInterface::ToJSVal(cx, value.GetOriginalString());
ScriptInterface::ToJSVal(cx, *vp.address(), value.GetOriginalString());
break;
}
@ -215,7 +218,7 @@ JSBool JSI_IGUIObject::getProperty(JSContext* cx, JSObject* obj, jsid id, jsval*
{
CStr value;
GUI<CStr>::GetSetting(e, propName, value);
*vp = ScriptInterface::ToJSVal(cx, value);
ScriptInterface::ToJSVal(cx, *vp.address(), value);
break;
}
@ -223,7 +226,7 @@ JSBool JSI_IGUIObject::getProperty(JSContext* cx, JSObject* obj, jsid id, jsval*
{
CStrW value;
GUI<CStrW>::GetSetting(e, propName, value);
*vp = ScriptInterface::ToJSVal(cx, value);
ScriptInterface::ToJSVal(cx, *vp.address(), value);
break;
}
@ -231,7 +234,7 @@ JSBool JSI_IGUIObject::getProperty(JSContext* cx, JSObject* obj, jsid id, jsval*
{
CGUISpriteInstance *value;
GUI<CGUISpriteInstance>::GetSettingPointer(e, propName, value);
*vp = ScriptInterface::ToJSVal(cx, value->GetName());
ScriptInterface::ToJSVal(cx, *vp.address(), value->GetName());
break;
}
@ -247,7 +250,7 @@ JSBool JSI_IGUIObject::getProperty(JSContext* cx, JSObject* obj, jsid id, jsval*
case EAlign_Center: word = "center"; break;
default: debug_warn(L"Invalid EAlign!"); word = "error"; break;
}
*vp = ScriptInterface::ToJSVal(cx, word);
ScriptInterface::ToJSVal(cx, *vp.address(), word);
break;
}
@ -263,7 +266,7 @@ JSBool JSI_IGUIObject::getProperty(JSContext* cx, JSObject* obj, jsid id, jsval*
case EVAlign_Center: word = "center"; break;
default: debug_warn(L"Invalid EVAlign!"); word = "error"; break;
}
*vp = ScriptInterface::ToJSVal(cx, word);
ScriptInterface::ToJSVal(cx, *vp.address(), word);
break;
}
@ -272,13 +275,14 @@ JSBool JSI_IGUIObject::getProperty(JSContext* cx, JSObject* obj, jsid id, jsval*
CGUIList value;
GUI<CGUIList>::GetSetting(e, propName, value);
JSObject *obj = JS_NewArrayObject(cx, 0, NULL);
*vp = OBJECT_TO_JSVAL(obj); // root it
for (size_t i = 0; i < value.m_Items.size(); ++i)
JS::RootedObject obj(cx, JS_NewArrayObject(cx, 0, NULL));
vp.set(JS::ObjectValue(*obj));
for (u32 i = 0; i < value.m_Items.size(); ++i)
{
jsval val = ScriptInterface::ToJSVal(cx, value.m_Items[i].GetOriginalString());
JS_SetElement(cx, obj, (jsint)i, &val);
JS::RootedValue val(cx);
ScriptInterface::ToJSVal(cx, val.get(), value.m_Items[i].GetOriginalString());
JS_SetElement(cx, obj, i, val.address());
}
break;
@ -294,7 +298,7 @@ JSBool JSI_IGUIObject::getProperty(JSContext* cx, JSObject* obj, jsid id, jsval*
}
}
JSBool JSI_IGUIObject::setProperty(JSContext* cx, JSObject* obj, jsid id, JSBool UNUSED(strict), jsval* vp)
JSBool JSI_IGUIObject::setProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JSBool UNUSED(strict), JS::MutableHandleValue vp)
{
IGUIObject* e = (IGUIObject*)JS_GetInstancePrivate(cx, obj, &JSI_IGUIObject::JSI_class, NULL);
if (!e)
@ -311,7 +315,7 @@ JSBool JSI_IGUIObject::setProperty(JSContext* cx, JSObject* obj, jsid id, JSBool
if (propName == "name")
{
std::string value;
if (!ScriptInterface::FromJSVal(cx, *vp, value))
if (!ScriptInterface::FromJSVal(cx, vp, value))
return JS_FALSE;
e->SetName(value);
return JS_TRUE;
@ -320,14 +324,14 @@ JSBool JSI_IGUIObject::setProperty(JSContext* cx, JSObject* obj, jsid id, JSBool
// Use onWhatever to set event handlers
if (propName.substr(0, 2) == "on")
{
if (!JSVAL_IS_OBJECT(*vp) || !JS_ObjectIsFunction(cx, JSVAL_TO_OBJECT(*vp)))
if (vp.isPrimitive() || vp.isNull() || !JS_ObjectIsFunction(cx, &vp.toObject()))
{
JS_ReportError(cx, "on- event-handlers must be functions");
return JS_FALSE;
}
CStr eventName (CStr(propName.substr(2)).LowerCase());
e->SetScriptHandler(eventName, JSVAL_TO_OBJECT(*vp));
e->SetScriptHandler(eventName, &vp.toObject());
return JS_TRUE;
}
@ -346,7 +350,7 @@ JSBool JSI_IGUIObject::setProperty(JSContext* cx, JSObject* obj, jsid id, JSBool
case GUIST_CStr:
{
std::string value;
if (!ScriptInterface::FromJSVal(cx, *vp, value))
if (!ScriptInterface::FromJSVal(cx, vp, value))
return JS_FALSE;
GUI<CStr>::SetSetting(e, propName, value);
@ -356,8 +360,8 @@ JSBool JSI_IGUIObject::setProperty(JSContext* cx, JSObject* obj, jsid id, JSBool
case GUIST_CStrW:
{
std::wstring value;
if (!ScriptInterface::FromJSVal(cx, *vp, value))
return JS_FALSE;
if (!ScriptInterface::FromJSVal(cx, vp, value))
return false;
GUI<CStrW>::SetSetting(e, propName, value);
break;
@ -366,8 +370,8 @@ JSBool JSI_IGUIObject::setProperty(JSContext* cx, JSObject* obj, jsid id, JSBool
case GUIST_CGUISpriteInstance:
{
std::string value;
if (!ScriptInterface::FromJSVal(cx, *vp, value))
return JS_FALSE;
if (!ScriptInterface::FromJSVal(cx, vp, value))
return false;
GUI<CGUISpriteInstance>::SetSetting(e, propName, CGUISpriteInstance(value));
break;
@ -376,7 +380,7 @@ JSBool JSI_IGUIObject::setProperty(JSContext* cx, JSObject* obj, jsid id, JSBool
case GUIST_CGUIString:
{
std::wstring value;
if (!ScriptInterface::FromJSVal(cx, *vp, value))
if (!ScriptInterface::FromJSVal(cx, vp, value))
return JS_FALSE;
CGUIString str;
@ -388,7 +392,7 @@ JSBool JSI_IGUIObject::setProperty(JSContext* cx, JSObject* obj, jsid id, JSBool
case GUIST_EAlign:
{
std::string value;
if (!ScriptInterface::FromJSVal(cx, *vp, value))
if (!ScriptInterface::FromJSVal(cx, vp, value))
return JS_FALSE;
EAlign a;
@ -407,7 +411,7 @@ JSBool JSI_IGUIObject::setProperty(JSContext* cx, JSObject* obj, jsid id, JSBool
case GUIST_EVAlign:
{
std::string value;
if (!ScriptInterface::FromJSVal(cx, *vp, value))
if (!ScriptInterface::FromJSVal(cx, vp, value))
return JS_FALSE;
EVAlign a;
@ -425,8 +429,8 @@ JSBool JSI_IGUIObject::setProperty(JSContext* cx, JSObject* obj, jsid id, JSBool
case GUIST_int:
{
int32 value;
if (JS_ValueToInt32(cx, *vp, &value) == JS_TRUE)
int value;
if (ScriptInterface::FromJSVal(cx, vp, value))
GUI<int>::SetSetting(e, propName, value);
else
{
@ -438,8 +442,8 @@ JSBool JSI_IGUIObject::setProperty(JSContext* cx, JSObject* obj, jsid id, JSBool
case GUIST_float:
{
jsdouble value;
if (JS_ValueToNumber(cx, *vp, &value) == JS_TRUE)
double value;
if (JS_ValueToNumber(cx, vp, &value) == true)
GUI<float>::SetSetting(e, propName, (float)value);
else
{
@ -452,8 +456,8 @@ JSBool JSI_IGUIObject::setProperty(JSContext* cx, JSObject* obj, jsid id, JSBool
case GUIST_bool:
{
JSBool value;
if (JS_ValueToBoolean(cx, *vp, &value) == JS_TRUE)
GUI<bool>::SetSetting(e, propName, value == JS_TRUE);
if (JS_ValueToBoolean(cx, vp, &value))
GUI<bool>::SetSetting(e, propName, value);
else
{
JS_ReportError(cx, "Cannot convert value to bool");
@ -464,10 +468,10 @@ JSBool JSI_IGUIObject::setProperty(JSContext* cx, JSObject* obj, jsid id, JSBool
case GUIST_CClientArea:
{
if (JSVAL_IS_STRING(*vp))
if (vp.isString())
{
std::wstring value;
if (!ScriptInterface::FromJSVal(cx, *vp, value))
if (!ScriptInterface::FromJSVal(cx, vp, value))
return JS_FALSE;
if (e->SetSetting(propName, value) != PSRETURN_OK)
@ -476,14 +480,13 @@ JSBool JSI_IGUIObject::setProperty(JSContext* cx, JSObject* obj, jsid id, JSBool
return JS_FALSE;
}
}
else if (JSVAL_IS_OBJECT(*vp) && JS_InstanceOf(cx, JSVAL_TO_OBJECT(*vp), &JSI_GUISize::JSI_class, NULL))
else if (vp.isObject() && JS_InstanceOf(cx, &vp.toObject(), &JSI_GUISize::JSI_class, NULL))
{
CClientArea area;
GUI<CClientArea>::GetSetting(e, propName, area);
JSObject* obj = JSVAL_TO_OBJECT(*vp);
ScriptInterface* pScriptInterface = ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface;
#define P(x, y, z) pScriptInterface->GetProperty(OBJECT_TO_JSVAL(obj), #z, area.x.y)
#define P(x, y, z) pScriptInterface->GetProperty(vp, #z, area.x.y)
P(pixel, left, left);
P(pixel, top, top);
P(pixel, right, right);
@ -506,10 +509,10 @@ JSBool JSI_IGUIObject::setProperty(JSContext* cx, JSObject* obj, jsid id, JSBool
case GUIST_CColor:
{
if (JSVAL_IS_STRING(*vp))
if (vp.isString())
{
std::wstring value;
if (!ScriptInterface::FromJSVal(cx, *vp, value))
if (!ScriptInterface::FromJSVal(cx, vp, value))
return JS_FALSE;
if (e->SetSetting(propName, value) != PSRETURN_OK)
@ -518,13 +521,14 @@ JSBool JSI_IGUIObject::setProperty(JSContext* cx, JSObject* obj, jsid id, JSBool
return JS_FALSE;
}
}
else if (JSVAL_IS_OBJECT(*vp) && JS_InstanceOf(cx, JSVAL_TO_OBJECT(*vp), &JSI_GUIColor::JSI_class, NULL))
else if (vp.isObject() && JS_InstanceOf(cx, &vp.toObject(), &JSI_GUIColor::JSI_class, NULL))
{
CColor colour;
JSObject* obj = JSVAL_TO_OBJECT(*vp);
jsval t; double s;
#define PROP(x) JS_GetProperty(cx, obj, #x, &t); \
JS_ValueToNumber(cx, t, &s); \
JS::RootedObject (cx, &vp.toObject());
JS::RootedValue t(cx);
double s;
#define PROP(x) JS_GetProperty(cx, obj, #x, t.address()); \
s = t.toDouble(); \
colour.x = (float)s
PROP(r); PROP(g); PROP(b); PROP(a);
#undef PROP
@ -541,16 +545,16 @@ JSBool JSI_IGUIObject::setProperty(JSContext* cx, JSObject* obj, jsid id, JSBool
case GUIST_CGUIList:
{
JSObject* obj = JSVAL_TO_OBJECT(*vp);
jsuint length;
if (JSVAL_IS_OBJECT(*vp) && JS_GetArrayLength(cx, obj, &length) == JS_TRUE)
u32 length;
if (vp.isObject() && JS_GetArrayLength(cx, &vp.toObject(), &length) == true)
{
CGUIList list;
for (int i=0; i<(int)length; ++i)
JS::RootedObject obj(cx, &vp.toObject());
for (u32 i=0; i<length; ++i)
{
jsval element;
if (! JS_GetElement(cx, obj, i, &element))
JS::RootedValue element(cx);
if (! JS_GetElement(cx, obj, i, element.address()))
{
JS_ReportError(cx, "Failed to get list element");
return JS_FALSE;
@ -562,7 +566,7 @@ JSBool JSI_IGUIObject::setProperty(JSContext* cx, JSObject* obj, jsid id, JSBool
CGUIString str;
str.SetValue(value);
list.m_Items.push_back(str);
}
@ -587,7 +591,7 @@ JSBool JSI_IGUIObject::setProperty(JSContext* cx, JSObject* obj, jsid id, JSBool
}
JSBool JSI_IGUIObject::construct(JSContext* cx, uintN argc, jsval* vp)
JSBool JSI_IGUIObject::construct(JSContext* cx, uint argc, jsval* vp)
{
if (argc == 0)
{
@ -595,13 +599,13 @@ JSBool JSI_IGUIObject::construct(JSContext* cx, uintN argc, jsval* vp)
return JS_FALSE;
}
JSObject* obj = JS_NewObject(cx, &JSI_IGUIObject::JSI_class, NULL, NULL);
JS::RootedObject obj(cx, JS_NewObject(cx, &JSI_IGUIObject::JSI_class, NULL, NULL));
// Store the IGUIObject in the JS object's 'private' area
IGUIObject* guiObject = (IGUIObject*)JSVAL_TO_PRIVATE(JS_ARGV(cx, vp)[0]);
JS_SetPrivate(cx, obj, guiObject);
JS_SetPrivate(obj, guiObject);
JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(obj));
JS_SET_RVAL(cx, vp, JS::ObjectValue(*obj));
return JS_TRUE;
}
@ -610,7 +614,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, uintN argc, jsval* vp)
JSBool JSI_IGUIObject::toString(JSContext* cx, uint argc, jsval* vp)
{
UNUSED2(argc);
@ -621,11 +625,11 @@ JSBool JSI_IGUIObject::toString(JSContext* cx, uintN argc, jsval* vp)
char buffer[256];
snprintf(buffer, 256, "[GUIObject: %s]", e->GetName().c_str());
buffer[255] = 0;
JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, buffer)));
JS_SET_RVAL(cx, vp, JS::StringValue(JS_NewStringCopyZ(cx, buffer)));
return JS_TRUE;
}
JSBool JSI_IGUIObject::focus(JSContext* cx, uintN argc, jsval* vp)
JSBool JSI_IGUIObject::focus(JSContext* cx, uint argc, jsval* vp)
{
UNUSED2(argc);
@ -639,7 +643,7 @@ JSBool JSI_IGUIObject::focus(JSContext* cx, uintN argc, jsval* vp)
return JS_TRUE;
}
JSBool JSI_IGUIObject::blur(JSContext* cx, uintN argc, jsval* vp)
JSBool JSI_IGUIObject::blur(JSContext* cx, uint argc, jsval* vp)
{
UNUSED2(argc);
@ -653,7 +657,7 @@ JSBool JSI_IGUIObject::blur(JSContext* cx, uintN argc, jsval* vp)
return JS_TRUE;
}
JSBool JSI_IGUIObject::getComputedSize(JSContext* cx, uintN argc, jsval* vp)
JSBool JSI_IGUIObject::getComputedSize(JSContext* cx, uint argc, jsval* vp)
{
UNUSED2(argc);
IGUIObject* e = (IGUIObject*)JS_GetInstancePrivate(cx, JS_THIS_OBJECT(cx, vp), &JSI_IGUIObject::JSI_class, NULL);
@ -663,14 +667,14 @@ JSBool JSI_IGUIObject::getComputedSize(JSContext* cx, uintN argc, jsval* vp)
e->UpdateCachedSize();
CRect size = e->m_CachedActualSize;
JSObject* obj = JS_NewObject(cx, NULL, NULL, NULL);
JS::RootedValue objVal(cx, JS::ObjectValue(*JS_NewObject(cx, NULL, NULL, NULL)));
try
{
ScriptInterface* pScriptInterface = ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface;
pScriptInterface->SetProperty(OBJECT_TO_JSVAL(obj), "left", size.left, false, true);
pScriptInterface->SetProperty(OBJECT_TO_JSVAL(obj), "right", size.right, false, true);
pScriptInterface->SetProperty(OBJECT_TO_JSVAL(obj), "top", size.top, false, true);
pScriptInterface->SetProperty(OBJECT_TO_JSVAL(obj), "bottom", size.bottom, false, true);
pScriptInterface->SetProperty(objVal, "left", size.left, false, true);
pScriptInterface->SetProperty(objVal, "right", size.right, false, true);
pScriptInterface->SetProperty(objVal, "top", size.top, false, true);
pScriptInterface->SetProperty(objVal, "bottom", size.bottom, false, true);
}
catch (PSERROR_Scripting_ConversionFailed&)
{
@ -678,6 +682,6 @@ JSBool JSI_IGUIObject::getComputedSize(JSContext* cx, uintN argc, jsval* vp)
return JS_FALSE;
}
JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(obj));
JS_SET_RVAL(cx, vp, objVal);
return JS_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, JSObject* obj, jsid id, jsval* vp);
JSBool setProperty(JSContext* cx, JSObject* obj, jsid id, JSBool strict, jsval* vp);
JSBool construct(JSContext* cx, uintN argc, jsval* vp);
JSBool toString(JSContext* cx, uintN argc, jsval* vp);
JSBool focus(JSContext* cx, uintN argc, jsval* vp);
JSBool blur(JSContext* cx, uintN argc, jsval* vp);
JSBool getComputedSize(JSContext* cx, uintN argc, jsval* vp);
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);
void init(ScriptInterface& scriptInterface);
}

View File

@ -69,7 +69,6 @@
#include "soundmanager/SoundManager.h"
#include "soundmanager/scripting/JSInterface_Sound.h"
#include "js/jsapi.h"
/*
* This file defines a set of functions that are available to GUI scripts, to allow
* interaction with the rest of the engine.
@ -88,10 +87,7 @@ namespace {
// 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 initData)
{
// TODO: Check the comment in CGUIManager::SwitchPage for a detailed explanation of this hack.
CScriptVal cloneSaveInitData;
cloneSaveInitData = g_GUI->GetScriptInterface()->CloneValueFromOtherContext(*(pCxPrivate->pScriptInterface), initData.get());
g_GUI->PushPage(name, g_GUI->GetScriptInterface()->WriteStructuredClone(cloneSaveInitData.get()));
g_GUI->PushPage(name, pCxPrivate->pScriptInterface->WriteStructuredClone(initData.get()));
}
void SwitchGuiPage(ScriptInterface::CxPrivate* pCxPrivate, std::wstring name, CScriptVal initData)
@ -557,7 +553,7 @@ void DebugWarn(ScriptInterface::CxPrivate* UNUSED(pCxPrivate))
void ForceGC(ScriptInterface::CxPrivate* pCxPrivate)
{
double time = timer_Time();
JS_GC(pCxPrivate->pScriptInterface->GetContext());
JS_GC(pCxPrivate->pScriptInterface->GetJSRuntime());
time = timer_Time() - time;
g_Console->InsertMessage(L"Garbage collection completed in: %f", time);
}

View File

@ -29,16 +29,16 @@
#include "lib/posix/posix_types.h"
// defines instead of typedefs so we can #undef in case of conflicts
typedef int8_t i8;
typedef int16_t i16;
typedef int32_t i32;
typedef int64_t i64;
#define i8 int8_t
#define i16 int16_t
#define i32 int32_t
#define i64 int64_t
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
#define u8 uint8_t
#define u16 uint16_t
#define u32 uint32_t
#define u64 uint64_t
typedef unsigned int uint;
#endif // #ifndef INCLUDED_TYPES

View File

@ -309,7 +309,7 @@ void XmppClient::SendIqGetRatingList()
void XmppClient::SendIqGameReport(ScriptInterface& scriptInterface, CScriptVal data)
{
glooxwrapper::JID xpartamuppJid(m_xpartamuppId);
jsval dataval = data.get();
JS::RootedValue dataval(scriptInterface.GetContext(), data.get());
// Setup some base stanza attributes
GameReport* game = new GameReport();
@ -343,7 +343,7 @@ void XmppClient::SendIqGameReport(ScriptInterface& scriptInterface, CScriptVal d
void XmppClient::SendIqRegisterGame(ScriptInterface& scriptInterface, CScriptVal data)
{
glooxwrapper::JID xpartamuppJid(m_xpartamuppId);
jsval dataval = data.get();
JS::RootedValue dataval(scriptInterface.GetContext(), data.get());
// Setup some base stanza attributes
GameListQuery* g = new GameListQuery();

View File

@ -184,8 +184,9 @@ static void PumpEvents()
PROFILE2("event");
if (g_GUI)
{
std::string data = g_GUI->GetScriptInterface()->StringifyJSON(
ScriptInterface::ToJSVal(g_GUI->GetScriptInterface()->GetContext(), ev));
JS::Value tmpVal;
ScriptInterface::ToJSVal(g_GUI->GetScriptInterface()->GetContext(), tmpVal, ev);
std::string data = g_GUI->GetScriptInterface()->StringifyJSON(tmpVal);
PROFILE2_ATTR("%s", data.c_str());
}
in_dispatch_event(&ev);

View File

@ -342,6 +342,9 @@ void* CNetServerWorker::RunThread(void* data)
void CNetServerWorker::Run()
{
// The script runtime uses the profiler and therefore the thread must be registered before the runtime is created
g_Profiler2.RegisterCurrentThread("Net server");
// 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());

View File

@ -560,6 +560,10 @@ static void ShutdownPs()
SAFE_DELETE(g_GUI);
SAFE_DELETE(g_Console);
// This is needed to ensure that no callbacks from the JSAPI try to use
// the profiler when it's already destructed
g_ScriptRuntime.reset();
// disable the special Windows cursor, or free textures for OGL cursors
cursor_draw(g_VFS, 0, g_mouse_x, g_yres-g_mouse_y, false);
@ -856,7 +860,7 @@ void Init(const CmdLineArgs& args, int flags)
g_Logger = new CLogger;
// Workaround until Simulation and AI use their own threads and also their own runtimes
g_ScriptRuntime = ScriptInterface::CreateRuntime(128 * 1024 * 1024);
g_ScriptRuntime = ScriptInterface::CreateRuntime(384 * 1024 * 1024);
// 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
@ -1100,7 +1104,7 @@ bool Autostart(const CmdLineArgs& args)
CStr seedArg = args.Get("autostart-random");
// Default seed is 0
uint32 seed = 0;
u32 seed = 0;
if (!seedArg.empty())
{
if (seedArg.compare("-1") == 0)

View File

@ -127,7 +127,7 @@ void CReplayPlayer::Replay()
new CProfileManager;
g_ScriptStatsTable = new CScriptStatsTable;
g_ProfileViewer.AddRootTable(g_ScriptStatsTable);
g_ScriptRuntime = ScriptInterface::CreateRuntime(128 * 1024 * 1024);
g_ScriptRuntime = ScriptInterface::CreateRuntime(384 * 1024 * 1024);
CGame game(true);
g_Game = &game;
@ -243,6 +243,10 @@ void CReplayPlayer::Replay()
timer_DisplayClientTotals();
// Must be explicitly destructed here to avoid callbacks from the JSAPI trying to use g_Profiler2 when
// it's already destructed.
g_ScriptRuntime.reset();
// Clean up
delete &g_TexMan;

View File

@ -68,8 +68,9 @@ static Status BuildDirEntListCB(const VfsPath& pathname, const CFileInfo& UNUSED
{
BuildDirEntListState* s = (BuildDirEntListState*)cbData;
jsval val = ScriptInterface::ToJSVal(s->cx, CStrW(pathname.string()));
JS_SetElement(s->cx, s->filename_array, s->cur_idx++, &val);
JS::RootedValue val(s->cx);
ScriptInterface::ToJSVal( s->cx, val.get(), CStrW(pathname.string()) );
JS_SetElement(s->cx, s->filename_array, s->cur_idx++, val.address());
return INFO::OK;
}
@ -155,7 +156,9 @@ CScriptVal JSI_VFS::ReadFile(ScriptInterface::CxPrivate* pCxPrivate, std::wstrin
contents.Replace("\r\n", "\n");
// Decode as UTF-8
return ScriptInterface::ToJSVal( pCxPrivate->pScriptInterface->GetContext(), contents.FromUTF8() );
JS::Value ret;
ScriptInterface::ToJSVal( pCxPrivate->pScriptInterface->GetContext(), ret, contents.FromUTF8() );
return ret;
}
@ -191,8 +194,9 @@ CScriptVal JSI_VFS::ReadFileLines(ScriptInterface::CxPrivate* pCxPrivate, std::w
while (std::getline(ss, line))
{
// Decode each line as UTF-8
jsval val = ScriptInterface::ToJSVal(cx, CStr(line).FromUTF8());
JS_SetElement(cx, line_array, cur_line++, &val);
JS::RootedValue val(cx);
ScriptInterface::ToJSVal(cx, val.get(), CStr(line).FromUTF8());
JS_SetElement(cx, line_array, cur_line++, val.address());
}
return OBJECT_TO_JSVAL( line_array );

View File

@ -40,21 +40,21 @@ void AutoGCRooter::Trace(JSTracer* trc)
for (size_t i = 0; i < m_Objects.size(); ++i)
{
JS_CALL_OBJECT_TRACER(trc, m_Objects[i], "AutoGCRooter object");
JS_CallObjectTracer(trc, &m_Objects[i], "AutoGCRooter object");
}
for (size_t i = 0; i < m_Vals.size(); ++i)
{
JS_CALL_VALUE_TRACER(trc, m_Vals[i], "AutoGCRooter val");
JS_CallValueTracer(trc, &m_Vals[i], "AutoGCRooter val");
}
for (size_t i = 0; i < m_IdArrays.size(); ++i)
{
for (jsint j = 0; j < m_IdArrays[i]->length; ++j)
for (int j = 0; j < JS_IdArrayLength(m_ScriptInterface.GetContext(), m_IdArrays[i]); ++j)
{
jsval val = JSVAL_VOID;
JS_IdToValue(m_ScriptInterface.GetContext(), m_IdArrays[i]->vector[j], &val);
JS_CALL_VALUE_TRACER(trc, val, "AutoGCRooter id array");
JS_IdToValue(m_ScriptInterface.GetContext(), JS_IdArrayGet(m_ScriptInterface.GetContext(), m_IdArrays[i], j), &val);
JS_CallValueTracer(trc, &val, "AutoGCRooter id array");
}
}

View File

@ -20,7 +20,6 @@
#include "scriptinterface/ScriptTypes.h"
#include "js/jsapi.h"
/**
* Helper for rooting large groups of script values.

View File

@ -26,7 +26,7 @@
#define NUMBERED_LIST_BALANCED(z, i, data) BOOST_PP_COMMA_IF(i) data##i
// Some other things
#define TYPED_ARGS(z, i, data) , T##i a##i
#define CONVERT_ARG(z, i, data) T##i a##i; if (! ScriptInterface::FromJSVal<T##i>(cx, i < argc ? JS_ARGV(cx, vp)[i] : JSVAL_VOID, a##i)) return JS_FALSE;
#define CONVERT_ARG(z, i, data) T##i a##i; if (! ScriptInterface::FromJSVal<T##i>(cx, i < argc ? JS_ARGV(cx, vp)[i] : JS::UndefinedValue(), 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, "
@ -51,14 +51,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, uintN argc, jsval* vp);
static JSBool 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, uintN argc, jsval* vp);
static JSBool callMethod(JSContext* cx, uint argc, jsval* vp);
BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
#undef OVERLOADS

View File

@ -27,7 +27,7 @@ struct ScriptInterface_NativeWrapper {
#define OVERLOADS(z, i, data) \
template<TYPENAME_T0_HEAD(z,i) typename F> \
static void call(JSContext* cx, jsval& rval, F fptr T0_A0(z,i)) { \
rval = ScriptInterface::ToJSVal<R>(cx, fptr(ScriptInterface::GetScriptInterfaceAndCBData(cx) A0_TAIL(z,i))); \
ScriptInterface::ToJSVal<R>(cx, rval, fptr(ScriptInterface::GetScriptInterfaceAndCBData(cx) A0_TAIL(z,i))); \
}
BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
@ -53,7 +53,7 @@ struct ScriptInterface_NativeMethodWrapper {
#define OVERLOADS(z, i, data) \
template<TYPENAME_T0_HEAD(z,i) typename F> \
static void call(JSContext* cx, jsval& rval, TC* c, F fptr T0_A0(z,i)) { \
rval = ScriptInterface::ToJSVal<R>(cx, (c->*fptr)( A0(z,i) )); \
ScriptInterface::ToJSVal<R>(cx, rval, (c->*fptr)( A0(z,i) )); \
}
BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
@ -79,11 +79,11 @@ struct ScriptInterface_NativeMethodWrapper<void, TC> {
#define SCRIPT_PROFILE \
if (g_ScriptProfilingEnabled) \
{ \
ENSURE(JSVAL_IS_OBJECT(JS_CALLEE(cx, vp)) && JS_ObjectIsFunction(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)))); \
ENSURE(JS_CALLEE(cx, vp).isObject() && JS_ObjectIsFunction(cx, &JS_CALLEE(cx, vp).toObject())); \
const char* name = "(unknown)"; \
jsval nameval; \
if (JS_GetReservedSlot(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)), 0, &nameval) \
&& !JSVAL_IS_VOID(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); \
}
@ -91,14 +91,14 @@ struct ScriptInterface_NativeMethodWrapper<void, TC> {
// 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, uintN argc, jsval* vp) { \
JSBool ScriptInterface::call(JSContext* cx, uint argc, jsval* vp) { \
UNUSED2(argc); \
SCRIPT_PROFILE \
BOOST_PP_REPEAT_##z (i, CONVERT_ARG, ~) \
jsval rval = JSVAL_VOID; \
ScriptInterface_NativeWrapper<R>::call(cx, rval, fptr A0_TAIL(z,i)); \
JS_SET_RVAL(cx, vp, rval); \
return (ScriptInterface::IsExceptionPending(cx) ? JS_FALSE : JS_TRUE); \
return !ScriptInterface::IsExceptionPending(cx); \
}
BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
#undef OVERLOADS
@ -106,17 +106,17 @@ 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, uintN argc, jsval* vp) { \
JSBool ScriptInterface::callMethod(JSContext* cx, uint argc, jsval* vp) { \
UNUSED2(argc); \
SCRIPT_PROFILE \
if (ScriptInterface::GetClass(cx, JS_THIS_OBJECT(cx, vp)) != CLS) return JS_FALSE; \
TC* c = static_cast<TC*>(ScriptInterface::GetPrivate(cx, JS_THIS_OBJECT(cx, vp))); \
if (! c) return JS_FALSE; \
if (ScriptInterface::GetClass(JS_THIS_OBJECT(cx, vp)) != CLS) return false; \
TC* c = static_cast<TC*>(ScriptInterface::GetPrivate(JS_THIS_OBJECT(cx, vp))); \
if (! c) return false; \
BOOST_PP_REPEAT_##z (i, CONVERT_ARG, ~) \
jsval rval = JSVAL_VOID; \
ScriptInterface_NativeMethodWrapper<R, TC>::call(cx, rval, c, fptr A0_TAIL(z,i)); \
JS_SET_RVAL(cx, vp, rval); \
return (ScriptInterface::IsExceptionPending(cx) ? JS_FALSE : JS_TRUE); \
return !ScriptInterface::IsExceptionPending(cx); \
}
BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
#undef OVERLOADS

View File

@ -32,88 +32,84 @@
template<> bool ScriptInterface::FromJSVal<bool>(JSContext* cx, jsval v, bool& out)
{
JSBool ret;
WARN_IF_NOT(JSVAL_IS_BOOLEAN(v), v);
if (!JS_ValueToBoolean(cx, v, &ret))
return false;
out = (ret ? true : false);
JSAutoRequest rq(cx);
WARN_IF_NOT(v.isBoolean(), v);
out = JS::ToBoolean(v);
return true;
}
template<> bool ScriptInterface::FromJSVal<float>(JSContext* cx, jsval v, float& out)
{
jsdouble ret;
WARN_IF_NOT(JSVAL_IS_NUMBER(v), v);
if (!JS_ValueToNumber(cx, v, &ret))
JSAutoRequest rq(cx);
double tmp;
WARN_IF_NOT(v.isNumber(), v);
if (!JS::ToNumber(cx, v, &tmp))
return false;
out = ret;
out = tmp;
return true;
}
template<> bool ScriptInterface::FromJSVal<double>(JSContext* cx, jsval v, double& out)
{
jsdouble ret;
WARN_IF_NOT(JSVAL_IS_NUMBER(v), v);
if (!JS_ValueToNumber(cx, v, &ret))
JSAutoRequest rq(cx);
WARN_IF_NOT(v.isNumber(), v);
if (!JS::ToNumber(cx, v, &out))
return false;
out = ret;
return true;
}
template<> bool ScriptInterface::FromJSVal<i32>(JSContext* cx, jsval v, i32& out)
{
int32 ret;
WARN_IF_NOT(JSVAL_IS_NUMBER(v), v);
if (!JS_ValueToECMAInt32(cx, v, &ret))
JSAutoRequest rq(cx);
WARN_IF_NOT(v.isNumber(), v);
if (!JS::ToInt32(cx, v, &out))
return false;
out = ret;
return true;
}
template<> bool ScriptInterface::FromJSVal<u32>(JSContext* cx, jsval v, u32& out)
{
uint32 ret;
WARN_IF_NOT(JSVAL_IS_NUMBER(v), v);
if (!JS_ValueToECMAUint32(cx, v, &ret))
JSAutoRequest rq(cx);
WARN_IF_NOT(v.isNumber(), v);
if (!JS::ToUint32(cx, v, &out))
return false;
out = ret;
return true;
}
template<> bool ScriptInterface::FromJSVal<u16>(JSContext* cx, jsval v, u16& out)
{
uint16 ret;
WARN_IF_NOT(JSVAL_IS_NUMBER(v), v);
if (!JS_ValueToUint16(cx, v, &ret))
JSAutoRequest rq(cx);
WARN_IF_NOT(v.isNumber(), v);
if (!JS::ToUint16(cx, v, &out))
return false;
out = ret;
return true;
}
template<> bool ScriptInterface::FromJSVal<u8>(JSContext* cx, jsval v, u8& out)
{
uint16 ret;
WARN_IF_NOT(JSVAL_IS_NUMBER(v), v);
if (!JS_ValueToUint16(cx, v, &ret))
JSAutoRequest rq(cx);
u16 tmp;
WARN_IF_NOT(v.isNumber(), v);
if (!JS::ToUint16(cx, v, &tmp))
return false;
out = (u8)ret;
out = (u8)tmp;
return true;
}
template<> bool ScriptInterface::FromJSVal<long>(JSContext* cx, jsval v, long& out)
{
int32 tmp;
JSBool ok = JS_ValueToInt32(cx, v, &tmp);
i64 tmp;
bool ok = JS::ToInt64(cx, v, &tmp);
out = (long)tmp;
return ok == JS_TRUE;
return ok;
}
template<> bool ScriptInterface::FromJSVal<unsigned long>(JSContext* cx, jsval v, unsigned long& out)
{
int32 tmp;
JSBool ok = JS_ValueToInt32(cx, v, &tmp);
u64 tmp;
bool ok = JS::ToUint64(cx, v, &tmp);
out = (unsigned long)tmp;
return ok == JS_TRUE;
return ok;
}
// see comment below (where the same preprocessor condition is used)
@ -121,29 +117,28 @@ template<> bool ScriptInterface::FromJSVal<unsigned long>(JSContext* cx, jsval v
template<> bool ScriptInterface::FromJSVal<size_t>(JSContext* cx, jsval v, size_t& out)
{
int temp;
if(!FromJSVal<int>(cx, v, temp))
int tmp;
if(!FromJSVal<int>(cx, v, tmp))
return false;
if(temp < 0)
return false;
out = (size_t)temp;
out = (size_t)tmp;
return true;
}
template<> bool ScriptInterface::FromJSVal<ssize_t>(JSContext* cx, jsval v, ssize_t& out)
{
int temp;
if(!FromJSVal<int>(cx, v, temp))
int tmp;
if(!FromJSVal<int>(cx, v, tmp))
return false;
if(temp < 0)
if(tmp < 0)
return false;
out = (ssize_t)temp;
out = (ssize_t)tmp;
return true;
}
#endif
// NOTE: we can't define a jsval specialisation, because that conflicts with integer types
template<> bool ScriptInterface::FromJSVal<CScriptVal>(JSContext* UNUSED(cx), jsval v, CScriptVal& out)
{
out = v;
@ -158,12 +153,13 @@ template<> bool ScriptInterface::FromJSVal<CScriptValRooted>(JSContext* cx, jsva
template<> bool ScriptInterface::FromJSVal<std::wstring>(JSContext* cx, jsval v, std::wstring& out)
{
JSAutoRequest rq(cx);
WARN_IF_NOT(JSVAL_IS_STRING(v) || JSVAL_IS_NUMBER(v), v); // allow implicit number conversions
JSString* ret = JS_ValueToString(cx, v);
if (!ret)
JSString* str = JS_ValueToString(cx, v);
if (!str)
FAIL("Argument must be convertible to a string");
size_t length;
const jschar* ch = JS_GetStringCharsAndLength(cx, ret, &length);
const jschar* ch = JS_GetStringCharsAndLength(cx, str, &length);
if (!ch)
FAIL("JS_GetStringsCharsAndLength failed"); // out of memory
out = std::wstring(ch, ch + length);
@ -181,14 +177,15 @@ template<> bool ScriptInterface::FromJSVal<Path>(JSContext* cx, jsval v, Path& o
template<> bool ScriptInterface::FromJSVal<std::string>(JSContext* cx, jsval v, std::string& out)
{
WARN_IF_NOT(JSVAL_IS_STRING(v) || JSVAL_IS_NUMBER(v), v); // allow implicit number conversions
JSString* ret = JS_ValueToString(cx, v);
if (!ret)
JSAutoRequest rq(cx);
WARN_IF_NOT(v.isString() || v.isNumber(), v); // allow implicit number conversions
JSString* str = JS_ValueToString(cx, v);
if (!str)
FAIL("Argument must be convertible to a string");
char* ch = JS_EncodeString(cx, ret); // chops off high byte of each jschar
char* ch = JS_EncodeString(cx, str); // chops off high byte of each jschar
if (!ch)
FAIL("JS_EncodeString failed"); // out of memory
out = std::string(ch, ch + JS_GetStringLength(ret));
out = std::string(ch, ch + JS_GetStringLength(str));
JS_free(cx, ch);
return true;
}
@ -205,22 +202,27 @@ template<> bool ScriptInterface::FromJSVal<CStrW>(JSContext* cx, jsval v, CStrW&
template<> bool ScriptInterface::FromJSVal<Entity>(JSContext* cx, jsval v, Entity& out)
{
JSObject* obj;
if (!JS_ValueToObject(cx, v, &obj) || obj == NULL)
JSAutoRequest rq(cx);
if (!v.isObject())
FAIL("Argument must be an object");
jsval templateName, id, player, position, rotation;
JS::RootedObject obj(cx, &v.toObject());
JS::RootedValue templateName(cx);
JS::RootedValue id(cx);
JS::RootedValue player(cx);
JS::RootedValue position(cx);
JS::RootedValue rotation(cx);
// TODO: Report type errors
if(!JS_GetProperty(cx, obj, "player", &player) || !FromJSVal(cx, player, out.playerID))
if(!JS_GetProperty(cx, obj, "player", player.address()) || !FromJSVal(cx, player, out.playerID))
FAIL("Failed to read Entity.player property");
if (!JS_GetProperty(cx, obj, "templateName", &templateName) || !FromJSVal(cx, templateName, out.templateName))
if (!JS_GetProperty(cx, obj, "templateName", templateName.address()) || !FromJSVal(cx, templateName, out.templateName))
FAIL("Failed to read Entity.templateName property");
if (!JS_GetProperty(cx, obj, "id", &id) || !FromJSVal(cx, id, out.entityID))
if (!JS_GetProperty(cx, obj, "id", id.address()) || !FromJSVal(cx, id, out.entityID))
FAIL("Failed to read Entity.id property");
if (!JS_GetProperty(cx, obj, "position", &position) || !FromJSVal(cx, position, out.position))
if (!JS_GetProperty(cx, obj, "position", position.address()) || !FromJSVal(cx, position, out.position))
FAIL("Failed to read Entity.position property");
if (!JS_GetProperty(cx, obj, "rotation", &rotation) || !FromJSVal(cx, rotation, out.rotation))
if (!JS_GetProperty(cx, obj, "rotation", rotation.address()) || !FromJSVal(cx, rotation, out.rotation))
FAIL("Failed to read Entity.rotation property");
return true;
@ -229,58 +231,49 @@ template<> bool ScriptInterface::FromJSVal<Entity>(JSContext* cx, jsval v, Entit
////////////////////////////////////////////////////////////////
// Primitive types:
template<> jsval ScriptInterface::ToJSVal<bool>(JSContext* UNUSED(cx), const bool& val)
template<> void ScriptInterface::ToJSVal<bool>(JSContext* UNUSED(cx), JS::Value& ret, const bool& val)
{
return val ? JSVAL_TRUE : JSVAL_FALSE;
ret = val ? JSVAL_TRUE : JSVAL_FALSE;
}
template<> jsval ScriptInterface::ToJSVal<float>(JSContext* cx, const float& val)
template<> void ScriptInterface::ToJSVal<float>(JSContext* UNUSED(cx), JS::Value& ret, const float& val)
{
jsval rval = JSVAL_VOID;
JS_NewNumberValue(cx, val, &rval); // ignore return value
return rval;
ret = JS::NumberValue(val);
}
template<> jsval ScriptInterface::ToJSVal<double>(JSContext* cx, const double& val)
template<> void ScriptInterface::ToJSVal<double>(JSContext* UNUSED(cx), JS::Value& ret, const double& val)
{
jsval rval = JSVAL_VOID;
JS_NewNumberValue(cx, val, &rval); // ignore return value
return rval;
ret = JS::NumberValue(val);
}
template<> jsval ScriptInterface::ToJSVal<i32>(JSContext* UNUSED(cx), const i32& val)
template<> void ScriptInterface::ToJSVal<i32>(JSContext* UNUSED(cx), JS::Value& ret, const i32& val)
{
cassert(JSVAL_INT_BITS == 32);
return INT_TO_JSVAL(val);
ret = JS::NumberValue(val);
}
template<> jsval ScriptInterface::ToJSVal<u16>(JSContext* UNUSED(cx), const u16& val)
template<> void ScriptInterface::ToJSVal<u16>(JSContext* UNUSED(cx), JS::Value& ret, const u16& val)
{
return INT_TO_JSVAL(val);
ret = JS::NumberValue(val);
}
template<> jsval ScriptInterface::ToJSVal<u8>(JSContext* UNUSED(cx), const u8& val)
template<> void ScriptInterface::ToJSVal<u8>(JSContext* UNUSED(cx), JS::Value& ret, const u8& val)
{
return INT_TO_JSVAL(val);
ret = JS::NumberValue(val);
}
template<> jsval ScriptInterface::ToJSVal<u32>(JSContext* cx, const u32& val)
template<> void ScriptInterface::ToJSVal<u32>(JSContext* UNUSED(cx), JS::Value& ret, const u32& val)
{
if (val <= JSVAL_INT_MAX)
return INT_TO_JSVAL(val);
jsval rval = JSVAL_VOID;
JS_NewNumberValue(cx, val, &rval); // ignore return value
return rval;
ret = JS::NumberValue(val);
}
template<> jsval ScriptInterface::ToJSVal<long>(JSContext* UNUSED(cx), const long& val)
template<> void ScriptInterface::ToJSVal<long>(JSContext* UNUSED(cx), JS::Value& ret, const long& val)
{
return INT_TO_JSVAL((int)val);
ret = JS::NumberValue((int)val);
}
template<> jsval ScriptInterface::ToJSVal<unsigned long>(JSContext* UNUSED(cx), const unsigned long& val)
template<> void ScriptInterface::ToJSVal<unsigned long>(JSContext* UNUSED(cx), JS::Value& ret, const unsigned long& val)
{
return INT_TO_JSVAL((int)val);
ret = JS::NumberValue((int)val);
}
// (s)size_t are considered to be identical to (unsigned) int by GCC and
@ -290,103 +283,118 @@ template<> jsval ScriptInterface::ToJSVal<unsigned long>(JSContext* UNUSED(cx),
// for some reason, x64 MSC treats size_t as distinct from unsigned long:
#if MSC_VERSION && ARCH_AMD64
template<> jsval ScriptInterface::ToJSVal<size_t>(JSContext* UNUSED(cx), const size_t& val)
template<> void ScriptInterface::ToJSVal<size_t>(JSContext* UNUSED(cx), JS::Value& ret, const size_t& val)
{
return INT_TO_JSVAL((int)val);
ret = JS::NumberValue((int)val);
}
template<> jsval ScriptInterface::ToJSVal<ssize_t>(JSContext* UNUSED(cx), const ssize_t& val)
template<> void ScriptInterface::ToJSVal<ssize_t>(JSContext* UNUSED(cx), JS::Value& ret, const ssize_t& val)
{
return INT_TO_JSVAL((int)val);
ret = JS::NumberValue((int)val);
}
#endif
// NOTE: we can't define a jsval specialisation, because that conflicts with integer types
template<> jsval ScriptInterface::ToJSVal<CScriptVal>(JSContext* UNUSED(cx), const CScriptVal& val)
template<> void ScriptInterface::ToJSVal<CScriptVal>(JSContext* UNUSED(cx), JS::Value& ret, const CScriptVal& val)
{
return val.get();
ret = val.get();
}
template<> jsval ScriptInterface::ToJSVal<CScriptValRooted>(JSContext* UNUSED(cx), const CScriptValRooted& val)
template<> void ScriptInterface::ToJSVal<CScriptValRooted>(JSContext* UNUSED(cx), JS::Value& ret, const CScriptValRooted& val)
{
return val.get();
ret = val.get();
}
template<> jsval ScriptInterface::ToJSVal<std::wstring>(JSContext* cx, const std::wstring& val)
template<> void ScriptInterface::ToJSVal<std::wstring>(JSContext* cx, JS::Value& 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());
if (str)
return STRING_TO_JSVAL(str);
return JSVAL_VOID;
ret = JS::StringValue(str);
else
ret = JS::UndefinedValue();
}
template<> jsval ScriptInterface::ToJSVal<Path>(JSContext* cx, const Path& val)
template<> void ScriptInterface::ToJSVal<Path>(JSContext* cx, JS::Value& ret, const Path& val)
{
return ToJSVal(cx, val.string());
ToJSVal(cx, ret, val.string());
}
template<> jsval ScriptInterface::ToJSVal<std::string>(JSContext* cx, const std::string& val)
template<> void ScriptInterface::ToJSVal<std::string>(JSContext* cx, JS::Value& ret, const std::string& val)
{
JSAutoRequest rq(cx);
JSString* str = JS_NewStringCopyN(cx, val.c_str(), val.length());
if (str)
return STRING_TO_JSVAL(str);
return JSVAL_VOID;
ret = JS::StringValue(str);
else
ret = JS::UndefinedValue();
}
template<> jsval ScriptInterface::ToJSVal<const wchar_t*>(JSContext* cx, const wchar_t* const& val)
template<> void ScriptInterface::ToJSVal<const wchar_t*>(JSContext* cx, JS::Value& ret, const wchar_t* const& val)
{
return ToJSVal(cx, std::wstring(val));
ToJSVal(cx, ret, std::wstring(val));
}
template<> jsval ScriptInterface::ToJSVal<const char*>(JSContext* cx, const char* const& val)
template<> void ScriptInterface::ToJSVal<const char*>(JSContext* cx, JS::Value& ret, const char* const& val)
{
JSAutoRequest rq(cx);
JSString* str = JS_NewStringCopyZ(cx, val);
if (str)
return STRING_TO_JSVAL(str);
return JSVAL_VOID;
ret = JS::StringValue(str);
else
ret = JS::UndefinedValue();
}
template<> jsval ScriptInterface::ToJSVal<CStrW>(JSContext* cx, const CStrW& val)
template<> void ScriptInterface::ToJSVal<CStrW>(JSContext* cx, JS::Value& ret, const CStrW& val)
{
return ToJSVal(cx, static_cast<const std::wstring&>(val));
ToJSVal(cx, ret, static_cast<const std::wstring&>(val));
}
template<> jsval ScriptInterface::ToJSVal<CStr8>(JSContext* cx, const CStr8& val)
template<> void ScriptInterface::ToJSVal<CStr8>(JSContext* cx, JS::Value& ret, const CStr8& val)
{
return ToJSVal(cx, static_cast<const std::string&>(val));
ToJSVal(cx, ret, static_cast<const std::string&>(val));
}
////////////////////////////////////////////////////////////////
// Compound types:
template<typename T> static jsval ToJSVal_vector(JSContext* cx, const std::vector<T>& val)
template<typename T> static void ToJSVal_vector(JSContext* cx, JS::Value& ret, const std::vector<T>& val)
{
JSObject* obj = JS_NewArrayObject(cx, (jsint)val.size(), NULL);
JSAutoRequest rq(cx);
JSObject* obj = JS_NewArrayObject(cx, val.size(), NULL);
if (!obj)
return JSVAL_VOID;
for (size_t i = 0; i < val.size(); ++i)
{
jsval el = ScriptInterface::ToJSVal<T>(cx, val[i]);
JS_SetElement(cx, obj, (jsint)i, &el);
ret = JS::UndefinedValue();
return;
}
return OBJECT_TO_JSVAL(obj);
for (u32 i = 0; i < val.size(); ++i)
{
JS::RootedValue el(cx);
ScriptInterface::ToJSVal<T>(cx, el.get(), val[i]);
JS_SetElement(cx, obj, i, el.address());
}
ret = JS::ObjectValue(*obj);
}
template<typename T> static bool FromJSVal_vector(JSContext* cx, jsval v, std::vector<T>& out)
{
JSAutoRequest rq(cx);
JSObject* obj;
if (!JS_ValueToObject(cx, v, &obj) || obj == NULL || !(JS_IsArrayObject(cx, obj) || js_IsTypedArray(obj)))
if (!v.isObject())
FAIL("Argument must be an array");
jsuint length;
obj = &v.toObject();
if (!(JS_IsArrayObject(cx, obj) || JS_IsTypedArrayObject(obj)))
FAIL("Argument must be an array");
u32 length;
if (!JS_GetArrayLength(cx, obj, &length))
FAIL("Failed to get array length");
out.reserve(length);
for (jsuint i = 0; i < length; ++i)
for (u32 i = 0; i < length; ++i)
{
jsval el;
if (!JS_GetElement(cx, obj, i, &el))
JS::RootedValue el(cx);
if (!JS_GetElement(cx, obj, i, el.address()))
FAIL("Failed to read array element");
T el2;
if (!ScriptInterface::FromJSVal<T>(cx, el, el2))
@ -399,9 +407,9 @@ template<typename T> static bool FromJSVal_vector(JSContext* cx, jsval v, std::v
// Instantiate various vector types:
#define VECTOR(T) \
template<> jsval ScriptInterface::ToJSVal<std::vector<T> >(JSContext* cx, const std::vector<T>& val) \
template<> void ScriptInterface::ToJSVal<std::vector<T> >(JSContext* cx, JS::Value& ret, const std::vector<T>& val) \
{ \
return ToJSVal_vector(cx, val); \
ToJSVal_vector(cx, ret, val); \
} \
template<> bool ScriptInterface::FromJSVal<std::vector<T> >(JSContext* cx, jsval v, std::vector<T>& out) \
{ \
@ -417,9 +425,9 @@ VECTOR(CScriptValRooted)
class IComponent;
template<> jsval ScriptInterface::ToJSVal<std::vector<IComponent*> >(JSContext* cx, const std::vector<IComponent*>& val)
template<> void ScriptInterface::ToJSVal<std::vector<IComponent*> >(JSContext* cx, JS::Value& ret, const std::vector<IComponent*>& val)
{
return ToJSVal_vector(cx, val);
ToJSVal_vector(cx, ret, val);
}
template<> bool ScriptInterface::FromJSVal<std::vector<Entity> >(JSContext* cx, jsval v, std::vector<Entity>& out)

View File

@ -25,13 +25,19 @@
#include "scriptinterface/ScriptTypes.h"
// Ignore some harmless warnings
#if GCC_VERSION >= 402 // (older GCCs don't support this pragma)
// 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
# pragma GCC diagnostic ignored "-Wunused-parameter"
# pragma GCC diagnostic ignored "-Wredundant-decls"
# if GCC_VERSION >= 408
# 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
@ -46,8 +52,9 @@
# define signbit std::signbit
#endif
#include "js/jstypedarray.h"
#include "js/jsdbgapi.h"
#include "jsfriendapi.h"
#include "jsdbgapi.h"
#include "js/GCAPI.h"
#undef signbit
@ -57,8 +64,10 @@
#if GCC_VERSION >= 402
# pragma GCC diagnostic warning "-Wunused-parameter"
# pragma GCC diagnostic warning "-Wredundant-decls"
# pragma GCC diagnostic warning "-Wnon-virtual-dtor"
# if GCC_VERSION >= 406
# pragma GCC diagnostic pop // restore user flags
// restore user flags (and we don't have to manually restore the warning levels for GCC >= 4.6)
# pragma GCC diagnostic pop
# endif
#endif

File diff suppressed because it is too large Load Diff

View File

@ -25,8 +25,6 @@
#include "ScriptTypes.h"
#include "ScriptVal.h"
#include "js/jsapi.h"
#include "ps/Errors.h"
ERROR_GROUP(Scripting);
ERROR_TYPE(Scripting, SetupFailed);
@ -140,7 +138,7 @@ public:
* Call a constructor function, equivalent to JS "new ctor(arg)".
* @return The new object; or JSVAL_VOID on failure, and logs an error message
*/
jsval CallConstructor(jsval ctor, jsval arg);
jsval CallConstructor(jsval ctor, uint argc, jsval argv);
/**
* Create an object as with CallConstructor except don't actually execute the
@ -243,6 +241,14 @@ public:
*/
template<typename T>
bool GetProperty(jsval obj, const char* name, T& out);
/**
* This function overload is used for JS::MutableHandleValue type.
* If we use JS::RootedValue with the GetProperty function template, it will generate an overload for the type
* JS::RootedValue*, but JS::MutableHandleValue needs to be used when passing JS::RootedValue& to a function.
* Check the SpiderMonkey rooting guide for details.
*/
bool GetPropertyJS(jsval obj, const char* name, JS::MutableHandleValue out);
/**
* Get the integer-named property on the given object.
@ -255,9 +261,9 @@ public:
*/
bool HasProperty(jsval obj, const char* name);
bool EnumeratePropertyNamesWithPrefix(jsval obj, const char* prefix, std::vector<std::string>& out);
bool EnumeratePropertyNamesWithPrefix(JS::HandleValue objVal, const char* prefix, std::vector<std::string>& out);
bool SetPrototype(jsval obj, jsval proto);
bool SetPrototype(JS::HandleValue obj, JS::HandleValue proto);
bool FreezeObject(jsval obj, bool deep);
@ -327,8 +333,11 @@ public:
/**
* Convert a C++ type to a jsval. (This might trigger GC. The return
* value must be rooted if you don't want it to be collected.)
* NOTE: We are passing the JS::Value by reference instead of returning it by value.
* The reason is a memory corruption problem that appears to be caused by a bug in Visual Studio.
* Details here: http://www.wildfiregames.com/forum/index.php?showtopic=17289&p=285921
*/
template<typename T> static jsval ToJSVal(JSContext* cx, T const& val);
template<typename T> static void ToJSVal(JSContext* cx, JS::Value& ret, T const& val);
AutoGCRooter* ReplaceAutoGCRooter(AutoGCRooter* rooter);
@ -338,9 +347,34 @@ public:
void DumpHeap();
/**
* MaybeGC tries to determine whether garbage collection in cx's runtime would free up enough memory to be worth the amount of time it would take
* MaybeGC tries to determine whether garbage collection in cx's runtime would free up enough memory to be worth the amount of time it would take.
* This calls JS_MaybeGC directly, which does not do incremental GC. Usually you should prefer MaybeIncrementalRuntimeGC.
*/
void MaybeGC();
/**
* MaybeIncrementalRuntimeGC tries to determine whether a runtime-wide garbage collection would free up enough memory to
* be worth the amount of time it would take. It does this with our own logic and NOT some predefined JSAPI logic because
* such functionality currently isn't available out of the box.
* It does incremental GC which means it will collect one slice each time it's called until the garbage collection is done.
* This can and should be called quite regularly. It shouldn't cost much performance because it tries to run a GC only if
* necessary.
*/
void MaybeIncrementalRuntimeGC();
/**
* Triggers a full non-incremental garbage collection immediately. That should only be required in special cases and normally
* you should try to use MaybeIncrementalRuntimeGC instead.
*/
void ForceGC();
/**
* MathRandom (this function) calls the random number generator assigned to this ScriptInterface instance and
* returns the generated number.
* Math_random (with underscore, not this function) is a global function, but different random number generators can be
* stored per ScriptInterface. It calls MathRandom of the current ScriptInterface instance.
*/
bool MathRandom(double& nbr);
/**
* Structured clones are a way to serialize 'simple' JS values into a buffer
@ -355,8 +389,7 @@ public:
public:
StructuredClone();
~StructuredClone();
JSContext* m_Context;
uint64* m_Data;
u64* m_Data;
size_t m_Size;
};
@ -364,18 +397,18 @@ public:
jsval ReadStructuredClone(const shared_ptr<StructuredClone>& ptr);
private:
bool CallFunction_(jsval val, const char* name, size_t argc, jsval* argv, jsval& ret);
bool CallFunction_(jsval val, const char* name, uint argc, jsval* argv, jsval& ret);
bool Eval_(const char* code, jsval& ret);
bool Eval_(const wchar_t* code, jsval& ret);
bool SetGlobal_(const char* name, jsval value, bool replace);
bool SetProperty_(jsval obj, const char* name, jsval value, bool readonly, bool enumerate);
bool SetProperty_(jsval obj, const wchar_t* name, jsval value, bool readonly, bool enumerate);
bool SetPropertyInt_(jsval obj, int name, jsval value, bool readonly, bool enumerate);
bool GetProperty_(jsval obj, const char* name, jsval& value);
bool GetPropertyInt_(jsval obj, int name, jsval& value);
bool GetProperty_(jsval obj, const char* name, JS::MutableHandleValue out);
bool GetPropertyInt_(jsval obj, int name, JS::MutableHandleValue value);
static bool IsExceptionPending(JSContext* cx);
static JSClass* GetClass(JSContext* cx, JSObject* obj);
static void* GetPrivate(JSContext* cx, JSObject* obj);
static JSClass* GetClass(JSObject* obj);
static void* GetPrivate(JSObject* obj);
class CustomType
{
@ -387,6 +420,7 @@ private:
void Register(const char* name, JSNative fptr, size_t nargs);
std::auto_ptr<ScriptInterface_impl> m;
boost::rand48* m_rng;
std::map<std::string, CustomType> m_CustomObjectTypes;
// The nasty macro/template bits are split into a separate file so you don't have to look at them
@ -425,7 +459,7 @@ bool ScriptInterface::CallFunctionVoid(jsval val, const char* name, const T0& a0
{
jsval jsRet;
jsval argv[1];
argv[0] = ToJSVal(GetContext(), a0);
ToJSVal(GetContext(), argv[0], a0);
return CallFunction_(val, name, 1, argv, jsRet);
}
@ -434,8 +468,8 @@ bool ScriptInterface::CallFunctionVoid(jsval val, const char* name, const T0& a0
{
jsval jsRet;
jsval argv[2];
argv[0] = ToJSVal(GetContext(), a0);
argv[1] = ToJSVal(GetContext(), a1);
ToJSVal(GetContext(), argv[0], a0);
ToJSVal(GetContext(), argv[1], a1);
return CallFunction_(val, name, 2, argv, jsRet);
}
@ -444,9 +478,9 @@ bool ScriptInterface::CallFunctionVoid(jsval val, const char* name, const T0& a0
{
jsval jsRet;
jsval argv[3];
argv[0] = ToJSVal(GetContext(), a0);
argv[1] = ToJSVal(GetContext(), a1);
argv[2] = ToJSVal(GetContext(), a2);
ToJSVal(GetContext(), argv[0], a0);
ToJSVal(GetContext(), argv[1], a1);
ToJSVal(GetContext(), argv[2], a2);
return CallFunction_(val, name, 3, argv, jsRet);
}
@ -455,7 +489,7 @@ bool ScriptInterface::CallFunction(jsval val, const char* name, const T0& a0, R&
{
jsval jsRet;
jsval argv[1];
argv[0] = ToJSVal(GetContext(), a0);
ToJSVal(GetContext(), argv[0], a0);
bool ok = CallFunction_(val, name, 1, argv, jsRet);
if (!ok)
return false;
@ -467,8 +501,8 @@ bool ScriptInterface::CallFunction(jsval val, const char* name, const T0& a0, co
{
jsval jsRet;
jsval argv[2];
argv[0] = ToJSVal(GetContext(), a0);
argv[1] = ToJSVal(GetContext(), a1);
ToJSVal(GetContext(), argv[0], a0);
ToJSVal(GetContext(), argv[1], a1);
bool ok = CallFunction_(val, name, 2, argv, jsRet);
if (!ok)
return false;
@ -480,9 +514,9 @@ bool ScriptInterface::CallFunction(jsval val, const char* name, const T0& a0, co
{
jsval jsRet;
jsval argv[3];
argv[0] = ToJSVal(GetContext(), a0);
argv[1] = ToJSVal(GetContext(), a1);
argv[2] = ToJSVal(GetContext(), a2);
ToJSVal(GetContext(), argv[0], a0);
ToJSVal(GetContext(), argv[1], a1);
ToJSVal(GetContext(), argv[2], a2);
bool ok = CallFunction_(val, name, 3, argv, jsRet);
if (!ok)
return false;
@ -494,10 +528,10 @@ bool ScriptInterface::CallFunction(jsval val, const char* name, const T0& a0, co
{
jsval jsRet;
jsval argv[4];
argv[0] = ToJSVal(GetContext(), a0);
argv[1] = ToJSVal(GetContext(), a1);
argv[2] = ToJSVal(GetContext(), a2);
argv[3] = ToJSVal(GetContext(), a3);
ToJSVal(GetContext(), argv[0], a0);
ToJSVal(GetContext(), argv[1], a1);
ToJSVal(GetContext(), argv[2], a2);
ToJSVal(GetContext(), argv[3], a3);
bool ok = CallFunction_(val, name, 4, argv, jsRet);
if (!ok)
return false;
@ -507,13 +541,17 @@ bool ScriptInterface::CallFunction(jsval val, const char* name, const T0& a0, co
template<typename T>
bool ScriptInterface::SetGlobal(const char* name, const T& value, bool replace)
{
return SetGlobal_(name, ToJSVal(GetContext(), value), replace);
JS::Value val;
ToJSVal(GetContext(), val, value);
return SetGlobal_(name, val, replace);
}
template<typename T>
bool ScriptInterface::SetProperty(jsval obj, const char* name, const T& value, bool readonly, bool enumerate)
{
return SetProperty_(obj, name, ToJSVal(GetContext(), value), readonly, enumerate);
JS::Value val;
ToJSVal(GetContext(), val, value);
return SetProperty_(obj, name, val, readonly, enumerate);
}
template<typename T>
@ -525,23 +563,28 @@ bool ScriptInterface::SetProperty(jsval obj, const wchar_t* name, const T& value
template<typename T>
bool ScriptInterface::SetPropertyInt(jsval obj, int name, const T& value, bool readonly, bool enumerate)
{
return SetPropertyInt_(obj, name, ToJSVal(GetContext(), value), readonly, enumerate);
JS::Value val;
ToJSVal(GetContext(), val, value);
return SetPropertyInt_(obj, name, val, readonly, enumerate);
}
template<typename T>
bool ScriptInterface::GetProperty(jsval obj, const char* name, T& out)
{
jsval val;
if (! GetProperty_(obj, name, val))
JSContext* cx = GetContext();
JSAutoRequest rq(cx);
JS::RootedValue val(cx);
if (! GetProperty_(obj, name, &val))
return false;
return FromJSVal(GetContext(), val, out);
return FromJSVal(cx, val, out);
}
template<typename T>
bool ScriptInterface::GetPropertyInt(jsval obj, int name, T& out)
{
jsval val;
if (! GetPropertyInt_(obj, name, val))
JSAutoRequest rq(GetContext());
JS::RootedValue val(GetContext());
if (! GetPropertyInt_(obj, name, &val))
return false;
return FromJSVal(GetContext(), val, out);
}

View File

@ -21,8 +21,6 @@
#include "scriptinterface/ScriptInterface.h"
#include "js/jsapi.h"
CScriptStatsTable* g_ScriptStatsTable;
enum

View File

@ -23,60 +23,53 @@
# ifndef WIN32
# define WIN32 // SpiderMonkey expects this
# endif
// The jsval struct type causes crashes due to weird miscompilation
// issues in (at least) VC2008, so force it to be the less-type-safe
// non-struct type instead
# define JS_NO_JSVAL_JSID_STRUCT_TYPES
// Make JS think the int8_t etc types are defined, since wposix_types.h emulates
// the ones that are needed and this avoids conflicting definitions
# define JS_SYS_TYPES_H_DEFINES_EXACT_SIZE_TYPES
#else // If not Windows, then Unix:
# define XP_UNIX
// In DEBUG mode, jsval defaults to struct types. Normally we build separate
// debug/release mode versions of the library, but when using --with-system-mozjs185
// it's always a release mode library, so we have to disable struct types for
// ABI compatibility
# if defined(DEBUG) && defined(WITH_SYSTEM_MOZJS185)
# define JS_NO_JSVAL_JSID_STRUCT_TYPES
# endif
#endif
// (we don't support XP_OS2 or XP_BEOS)
// Guess whether the library was compiled with the release-mode or debug-mode ABI
// (for JS_DumpHeap etc)
#if defined(DEBUG) && !defined(WITH_SYSTEM_MOZJS185)
#if defined(DEBUG) && !defined(WITH_SYSTEM_MOZJS24)
# define MOZJS_DEBUG_ABI 1
#else
# define MOZJS_DEBUG_ABI 0
#endif
// SpiderMonkey wants the DEBUG flag
#ifndef NDEBUG
# ifndef DEBUG
# define DEBUG
# endif
#endif
// Ignore some harmless warnings triggered by jsapi.h
#if GCC_VERSION >= 402 // (older GCCs don't support this pragma)
// 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
# 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
#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:4512) // "assignment operator could not be generated"
# 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'"
#endif
#include "js/jsapi.h"
#include "jspubtd.h"
#include "jsapi.h"
#if MSC_VERSION
# pragma warning(pop)
@ -84,15 +77,34 @@
#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
#endif
#if JS_VERSION != 185
#error Your compiler is trying to use an incorrect version of the SpiderMonkey library.
#if MOZJS_MAJOR_VERSION != 24
#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.
#error Make sure you have got all the right files and include paths.
#endif
#if MOZJS_MINOR_VERSION != 2
#error Your compiler is trying to use an untested minor version of the SpiderMonkey library.
#error If you are a package maintainer, please make sure to check very carefully that this version does not change
#error the behaviour of the code executed by SpiderMonkey.
#error Different parts of the game (e.g. the multiplayer mode) rely on deterministic behaviour of the JavaScript engine.
#error A simple way for testing this would be playing a network game with one player using the old version and one player using the new version.
#error Another way for testing is running replays and comparing the final hash (check trac.wildfiregames.com/wiki/Debugging#Replaymode).
#error For more information check this link: trac.wildfiregames.com/wiki/Debugging#Outofsync
#endif
class ScriptInterface;
class CScriptVal;
class CScriptValRooted;

View File

@ -17,19 +17,24 @@
#include "precompiled.h"
#include "ScriptInterface.h"
#include "ScriptVal.h"
#include "js/jsapi.h"
struct Unrooter
{
Unrooter(JSContext* cx) : cx(cx) { }
void operator()(jsval* p) { JS_RemoveValueRoot(cx, p); delete p; }
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));
@ -37,6 +42,7 @@ CScriptValRooted::CScriptValRooted(JSContext* cx, jsval val)
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));
@ -64,33 +70,3 @@ bool CScriptValRooted::uninitialised() const
{
return !m_Val;
}
AutoJSIdArray::AutoJSIdArray(JSContext* cx, JSIdArray* ida) :
m_Context(cx), m_IdArray(ida)
{
}
AutoJSIdArray::~AutoJSIdArray()
{
if (m_IdArray)
JS_DestroyIdArray(m_Context, m_IdArray);
}
JSIdArray* AutoJSIdArray::get() const
{
return m_IdArray;
}
size_t AutoJSIdArray::length() const
{
if (!m_IdArray)
return 0;
return m_IdArray->length;
}
jsid AutoJSIdArray::operator[](size_t i) const
{
if (!(m_IdArray && i < (size_t)m_IdArray->length))
return JSID_VOID;
return m_IdArray->vector[i];
}

View File

@ -78,23 +78,4 @@ private:
boost::shared_ptr<jsval> m_Val;
};
/**
* RAII wrapper for JSIdArray*
*/
class AutoJSIdArray
{
NONCOPYABLE(AutoJSIdArray);
public:
AutoJSIdArray(JSContext* cx, JSIdArray* ida);
~AutoJSIdArray();
JSIdArray* get() const;
size_t length() const;
jsid operator[](size_t i) const;
private:
JSContext* m_Context;
JSIdArray* m_IdArray;
};
#endif // INCLUDED_SCRIPTVAL

View File

@ -23,7 +23,7 @@
#include "maths/MathUtil.h"
#include "ps/CLogger.h"
#include "js/jsapi.h"
#include "jsapi.h"
class TestScriptConversions : public CxxTest::TestSuite
{
@ -32,13 +32,15 @@ class TestScriptConversions : public CxxTest::TestSuite
{
ScriptInterface script("Test", "Test", ScriptInterface::CreateRuntime());
JSContext* cx = script.GetContext();
JSAutoRequest rq(cx);
jsval v1 = ScriptInterface::ToJSVal(cx, value);
JS::Value v1;
ScriptInterface::ToJSVal(cx, v1, value);
// We want to convert values to strings, but can't just call toSource() on them
// since they might not be objects. So just use uneval.
std::string source;
TS_ASSERT(script.CallFunction(OBJECT_TO_JSVAL(JS_GetGlobalObject(cx)), "uneval", CScriptVal(v1), source));
TS_ASSERT(script.CallFunction(OBJECT_TO_JSVAL(JS_GetGlobalForScopeChain(cx)), "uneval", CScriptVal(v1), source));
TS_ASSERT_STR_EQUALS(source, expected);
}
@ -48,11 +50,13 @@ class TestScriptConversions : public CxxTest::TestSuite
{
ScriptInterface script("Test", "Test", ScriptInterface::CreateRuntime());
JSContext* cx = script.GetContext();
JSAutoRequest rq(cx);
jsval v1 = ScriptInterface::ToJSVal(cx, value);
JS::Value v1;
ScriptInterface::ToJSVal(cx, v1, value);
std::string source;
TS_ASSERT(script.CallFunction(OBJECT_TO_JSVAL(JS_GetGlobalObject(cx)), "uneval", CScriptVal(v1), source));
TS_ASSERT(script.CallFunction(OBJECT_TO_JSVAL(JS_GetGlobalForScopeChain(cx)), "uneval", CScriptVal(v1), source));
if (expected)
TS_ASSERT_STR_EQUALS(source, expected);
@ -101,12 +105,12 @@ public:
roundtrip<std::string>("", "\"\"");
roundtrip<std::string>("test", "\"test\"");
roundtrip<std::string>(s1, "\"t\\0st\"");
roundtrip<std::string>(s1, "\"t\\x00st\"");
// TODO: should test non-ASCII strings
roundtrip<std::wstring>(L"", "\"\"");
roundtrip<std::wstring>(L"test", "\"test\"");
roundtrip<std::wstring>(w1, "\"t\\0st\"");
roundtrip<std::wstring>(w1, "\"t\\x00st\"");
// TODO: should test non-ASCII strings
convert_to<const char*>("", "\"\"");
@ -123,19 +127,29 @@ public:
{
ScriptInterface script("Test", "Test", ScriptInterface::CreateRuntime());
JSContext* cx = script.GetContext();
JSAutoRequest rq(cx);
// using new uninitialized variables each time to be sure the test doesn't succeeed if ToJSVal doesn't touch the value at all.
JS::Value val0, val1, val2, val3, val4, val5, val6, val7, val8;
ScriptInterface::ToJSVal<i32>(cx, val0, 0);
ScriptInterface::ToJSVal<i32>(cx, val1, 2147483646); // JSVAL_INT_MAX-1
ScriptInterface::ToJSVal<i32>(cx, val2, 2147483647); // JSVAL_INT_MAX
ScriptInterface::ToJSVal<i32>(cx, val3, -2147483647); // JSVAL_INT_MIN+1
ScriptInterface::ToJSVal<i32>(cx, val4, -(i64)2147483648u); // JSVAL_INT_MIN
TS_ASSERT(JSVAL_IS_INT(val0));
TS_ASSERT(JSVAL_IS_INT(val1));
TS_ASSERT(JSVAL_IS_INT(val2));
TS_ASSERT(JSVAL_IS_INT(val3));
TS_ASSERT(JSVAL_IS_INT(val4));
TS_ASSERT(JSVAL_IS_INT(ScriptInterface::ToJSVal<i32>(cx, 0)));
TS_ASSERT(JSVAL_IS_INT(ScriptInterface::ToJSVal<i32>(cx, 2147483646))); // JSVAL_INT_MAX-1
TS_ASSERT(JSVAL_IS_INT(ScriptInterface::ToJSVal<i32>(cx, 2147483647))); // JSVAL_INT_MAX
TS_ASSERT(JSVAL_IS_INT(ScriptInterface::ToJSVal<i32>(cx, -2147483647))); // JSVAL_INT_MIN+1
TS_ASSERT(JSVAL_IS_INT(ScriptInterface::ToJSVal<i32>(cx, -(i64)2147483648u))); // JSVAL_INT_MIN
TS_ASSERT(JSVAL_IS_INT(ScriptInterface::ToJSVal<u32>(cx, 0)));
TS_ASSERT(JSVAL_IS_INT(ScriptInterface::ToJSVal<u32>(cx, 2147483646u))); // JSVAL_INT_MAX-1
TS_ASSERT(JSVAL_IS_INT(ScriptInterface::ToJSVal<u32>(cx, 2147483647u))); // JSVAL_INT_MAX
TS_ASSERT(JSVAL_IS_DOUBLE(ScriptInterface::ToJSVal<u32>(cx, 2147483648u))); // JSVAL_INT_MAX+1
ScriptInterface::ToJSVal<u32>(cx, val5, 0);
ScriptInterface::ToJSVal<u32>(cx, val6, 2147483646u); // JSVAL_INT_MAX-1
ScriptInterface::ToJSVal<u32>(cx, val7, 2147483647u); // JSVAL_INT_MAX
ScriptInterface::ToJSVal<u32>(cx, val8, 2147483648u); // JSVAL_INT_MAX+1
TS_ASSERT(JSVAL_IS_INT(val5));
TS_ASSERT(JSVAL_IS_INT(val6));
TS_ASSERT(JSVAL_IS_INT(val7));
TS_ASSERT(JSVAL_IS_DOUBLE(val8));
}
void test_nonfinite()
@ -146,9 +160,12 @@ public:
ScriptInterface script("Test", "Test", ScriptInterface::CreateRuntime());
JSContext* cx = script.GetContext();
JSAutoRequest rq(cx);
float f = 0;
TS_ASSERT(ScriptInterface::FromJSVal(cx, ScriptInterface::ToJSVal(cx, NAN), f));
JS::Value testNANVal;
ScriptInterface::ToJSVal(cx, testNANVal, NAN);
TS_ASSERT(ScriptInterface::FromJSVal(cx, testNANVal, f));
TS_ASSERT(isnan(f));
}

View File

@ -61,8 +61,9 @@ public:
void test_clone_basic()
{
ScriptInterface script1("Test", "Test", ScriptInterface::CreateRuntime());
ScriptInterface script2("Test", "Test", ScriptInterface::CreateRuntime());
shared_ptr<ScriptRuntime> runtime = ScriptInterface::CreateRuntime();
ScriptInterface script1("Test", "Test", runtime);
ScriptInterface script2("Test", "Test", runtime);
CScriptVal obj1;
TS_ASSERT(script1.Eval("({'x': 123, 'y': [1, 1.5, '2', 'test', undefined, null, true, false]})", obj1));
@ -78,8 +79,9 @@ public:
{
// The tests should be run with JS_SetGCZeal so this can try to find GC bugs
ScriptInterface script1("Test", "Test", ScriptInterface::CreateRuntime());
ScriptInterface script2("Test", "Test", ScriptInterface::CreateRuntime());
shared_ptr<ScriptRuntime> runtime = ScriptInterface::CreateRuntime();
ScriptInterface script1("Test", "Test", runtime);
ScriptInterface script2("Test", "Test", runtime);
CScriptVal obj1;
TS_ASSERT(script1.Eval("var s = '?'; var v = ({get x() { return 123 }, 'y': {'w':{get z() { delete v.y; delete v.n; v = null; s += s; return 4 }}}, 'n': 100}); v", obj1));
@ -88,22 +90,33 @@ public:
std::string source;
TS_ASSERT(script2.CallFunction(obj2.get(), "toSource", source));
TS_ASSERT_STR_EQUALS(source, "({x:123, y:{w:{z:4}}, n:(void 0)})");
TS_ASSERT_STR_EQUALS(source, "({x:123, y:{w:{z:4}}})");
}
void test_clone_cyclic()
{
ScriptInterface script1("Test", "Test", ScriptInterface::CreateRuntime());
ScriptInterface script2("Test", "Test", ScriptInterface::CreateRuntime());
shared_ptr<ScriptRuntime> runtime = ScriptInterface::CreateRuntime();
ScriptInterface script1("Test", "Test", runtime);
ScriptInterface script2("Test", "Test", runtime);
CScriptVal obj1;
TS_ASSERT(script1.Eval("var x = []; x[0] = x; ({'a': x, 'b': x})", obj1));
CScriptVal obj2 = script2.CloneValueFromOtherContext(script1, obj1.get());
std::string source;
TS_ASSERT(script2.CallFunction(obj2.get(), "toSource", source));
TS_ASSERT_STR_EQUALS(source, "({a:#1=[#1#], b:#1#})");
// Use JSAPI function to check if the values of the properties "a", "b" are equals a.x[0]
JSContext* cx2 = script2.GetContext();
JSAutoRequest rq(cx2);
JS::RootedValue prop_a(cx2);
JS::RootedValue prop_b(cx2);
JS::RootedValue prop_x1(cx2);
TS_ASSERT(JS_GetProperty(cx2, &(obj2.get().toObject()), "a", prop_a.address()));
TS_ASSERT(JS_GetProperty(cx2, &(obj2.get().toObject()), "b", prop_b.address()));
TS_ASSERT(prop_a.get().isObject());
TS_ASSERT(prop_b.get().isObject());
TS_ASSERT(JS_GetProperty(cx2, &(prop_a.get().toObject()), "0", prop_x1.address()));
TS_ASSERT_EQUALS(prop_x1.get(), prop_a.get());
TS_ASSERT_EQUALS(prop_x1.get(), prop_b.get());
}
void test_random()

View File

@ -20,7 +20,7 @@
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/ScriptVal.h"
#include "js/jsapi.h"
#include "jsapi.h"
class TestScriptVal : public CxxTest::TestSuite
{
@ -29,18 +29,19 @@ public:
{
ScriptInterface script("Test", "Test", ScriptInterface::CreateRuntime());
JSContext* cx = script.GetContext();
JSAutoRequest rq(cx);
JSObject* obj = JS_NewObject(cx, NULL, NULL, NULL);
TS_ASSERT(obj);
CScriptValRooted root(cx, OBJECT_TO_JSVAL(obj));
JS_GC(cx);
JS_GC(script.GetJSRuntime());
jsval val = INT_TO_JSVAL(123);
TS_ASSERT(JS_SetProperty(cx, obj, "test", &val));
JS_GC(cx);
JS_GC(script.GetJSRuntime());
jsval rval;
TS_ASSERT(JS_GetProperty(cx, obj, "test", &rval));

View File

@ -19,6 +19,8 @@
#include "Simulation2.h"
#include "scriptinterface/ScriptInterface.h"
#include "simulation2/MessageTypes.h"
#include "simulation2/system/ComponentManager.h"
#include "simulation2/system/ParamNode.h"
@ -451,8 +453,8 @@ void CSimulation2Impl::Update(int turnLength, const std::vector<SimulationComman
// Run the GC occasionally
// (TODO: we ought to schedule this for a frame where we're not
// running the sim update, to spread the load)
if (m_TurnNumber % 10 == 0)
m_ComponentManager.GetScriptInterface().MaybeGC();
if (m_TurnNumber % 1 == 0)
m_ComponentManager.GetScriptInterface().MaybeIncrementalRuntimeGC();
if (m_EnableOOSLog)
DumpState();

View File

@ -152,7 +152,9 @@ private:
ENSURE(m_Worker.m_HasLoadedEntityTemplates);
m_ScriptInterface->SetProperty(settings.get(), "templates", m_Worker.m_EntityTemplates, false);
obj = m_ScriptInterface->CallConstructor(ctor.get(), settings.get());
JS::AutoValueVector argv(m_ScriptInterface->GetContext());
argv.append(settings.get());
obj = m_ScriptInterface->CallConstructor(ctor.get(), argv.length(), argv.handleAt(0));
}
else
{
@ -209,12 +211,7 @@ public:
};
CAIWorker() :
// TODO: Passing a 32 MB argument to CreateRuntime() is a temporary fix
// to prevent frequent AI out-of-memory crashes. The argument should be
// removed as soon whenever the new pathfinder is committed
// And the AIs can stop relying on their own little hands.
m_ScriptRuntime(ScriptInterface::CreateRuntime(33554432)),
m_ScriptInterface(new ScriptInterface("Engine", "AI", m_ScriptRuntime)),
m_ScriptInterface(new ScriptInterface("Engine", "AI", g_ScriptRuntime)),
m_TurnNum(0),
m_CommandsComputed(true),
m_HasLoadedEntityTemplates(false),
@ -305,7 +302,7 @@ public:
static void ForceGC(ScriptInterface::CxPrivate* pCxPrivate)
{
PROFILE3("AI compute GC");
JS_GC(pCxPrivate->pScriptInterface->GetContext());
JS_GC(pCxPrivate->pScriptInterface->GetJSRuntime());
}
/**
@ -388,7 +385,8 @@ public:
for (size_t i = 0; i < m_Players.size(); ++i)
{
jsval val = m_ScriptInterface->ToJSVal(m_ScriptInterface->GetContext(), m_Players[i]->m_Player);
JS::Value val;
m_ScriptInterface->ToJSVal(m_ScriptInterface->GetContext(), val, m_Players[i]->m_Player);
m_ScriptInterface->SetPropertyInt(playersID.get(), i, CScriptVal(val), true);
}
@ -407,7 +405,10 @@ public:
m_ScriptInterface->Eval("({})", fakeTech);
m_ScriptInterface->SetProperty(settings.get(), "techTemplates", fakeTech, false);
}
m_SharedAIObj = CScriptValRooted(m_ScriptInterface->GetContext(),m_ScriptInterface->CallConstructor(ctor.get(), settings.get()));
JS::AutoValueVector argv(m_ScriptInterface->GetContext());
argv.append(settings.get());
m_SharedAIObj = CScriptValRooted(m_ScriptInterface->GetContext(),m_ScriptInterface->CallConstructor(ctor.get(), argv.length(), argv.handleAt(0)));
if (m_SharedAIObj.undefined())
@ -429,8 +430,6 @@ public:
if (!m_HasSharedComponent)
m_HasSharedComponent = ai->m_UseSharedComponent;
m_ScriptInterface->MaybeGC();
m_Players.push_back(ai);
return true;
@ -444,15 +443,17 @@ public:
CScriptVal state = m_ScriptInterface->ReadStructuredClone(gameState);
JSContext* cx = m_ScriptInterface->GetContext();
m_PassabilityMapVal = CScriptValRooted(cx, ScriptInterface::ToJSVal(cx, passabilityMap));
m_TerritoryMapVal = CScriptValRooted(cx, ScriptInterface::ToJSVal(cx, territoryMap));
JS::RootedValue tmpVal(cx);
ScriptInterface::ToJSVal(cx, tmpVal.get(), passabilityMap);
m_PassabilityMapVal = CScriptValRooted(cx, tmpVal.get());
ScriptInterface::ToJSVal(cx, tmpVal.get(), territoryMap);
m_TerritoryMapVal = CScriptValRooted(cx, tmpVal);
if (m_HasSharedComponent)
{
m_ScriptInterface->SetProperty(state.get(), "passabilityMap", m_PassabilityMapVal, true);
m_ScriptInterface->SetProperty(state.get(), "territoryMap", m_TerritoryMapVal, true);
m_ScriptInterface->CallFunctionVoid(m_SharedAIObj.get(), "init", state);
m_ScriptInterface->MaybeGC();
for (size_t i = 0; i < m_Players.size(); ++i)
{
@ -468,21 +469,24 @@ 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;
JSContext* cx = m_ScriptInterface->GetContext();
m_PassabilityMapVal = CScriptValRooted(cx, ScriptInterface::ToJSVal(cx, m_PassabilityMap));
JS::RootedValue tmpVal(cx);
ScriptInterface::ToJSVal(cx, tmpVal.get(), m_PassabilityMap);
m_PassabilityMapVal = CScriptValRooted(cx, tmpVal);
}
if (territoryMapDirty)
{
m_TerritoryMap = territoryMap;
JSContext* cx = m_ScriptInterface->GetContext();
m_TerritoryMapVal = CScriptValRooted(cx, ScriptInterface::ToJSVal(cx, m_TerritoryMap));
JS::RootedValue tmpVal(cx);
ScriptInterface::ToJSVal(cx, tmpVal.get(), m_TerritoryMap);
m_TerritoryMapVal = CScriptValRooted(cx, tmpVal);
}
m_CommandsComputed = false;
@ -705,20 +709,7 @@ private:
}
void PerformComputation()
{
if (m_Players.size() == 0)
{
// Run the GC every so often.
// (This isn't particularly necessary, but it makes profiling clearer
// since it avoids random GC delays while running other scripts)
if (m_TurnNum++ % 50 == 0)
{
PROFILE3("AI compute GC");
m_ScriptInterface->MaybeGC();
}
return;
}
{
// Deserialize the game state, to pass to the AI's HandleMessage
CScriptVal state;
{
@ -752,23 +743,6 @@ private:
else
m_Players[i]->Run(state, m_Players[i]->m_Player);
}
// Run GC if we are about to overflow
if (JS_GetGCParameter(m_ScriptInterface->GetJSRuntime(), JSGC_BYTES) > 33000000)
{
PROFILE3("AI compute GC");
JS_GC(m_ScriptInterface->GetContext());
}
// Run the GC every so often.
// (This isn't particularly necessary, but it makes profiling clearer
// since it avoids random GC delays while running other scripts)
/*if (m_TurnNum++ % 20 == 0)
{
PROFILE3("AI compute GC");
m_ScriptInterface->MaybeGC();
}*/
}
shared_ptr<ScriptRuntime> m_ScriptRuntime;

View File

@ -38,23 +38,30 @@ CScriptVal ICmpFootprint::GetShape_wrapper()
if (shape == CIRCLE)
{
jsval ptype = ScriptInterface::ToJSVal<std::string>(cx, "circle");
jsval pradius = ScriptInterface::ToJSVal(cx, size0);
jsval pheight = ScriptInterface::ToJSVal(cx, height);
JS_SetProperty(cx, obj, "type", &ptype);
JS_SetProperty(cx, obj, "radius", &pradius);
JS_SetProperty(cx, obj, "height", &pheight);
JS::RootedValue ptype(cx);
JS::RootedValue pradius(cx);
JS::RootedValue pheight(cx);
ScriptInterface::ToJSVal<std::string>(cx, ptype.get(), "circle");
ScriptInterface::ToJSVal(cx, pradius.get(), size0);
ScriptInterface::ToJSVal(cx, pheight.get(), height);
JS_SetProperty(cx, obj, "type", ptype.address());
JS_SetProperty(cx, obj, "radius", pradius.address());
JS_SetProperty(cx, obj, "height", pheight.address());
}
else
{
jsval ptype = ScriptInterface::ToJSVal<std::string>(cx, "square");
jsval pwidth = ScriptInterface::ToJSVal(cx, size0);
jsval pdepth = ScriptInterface::ToJSVal(cx, size1);
jsval pheight = ScriptInterface::ToJSVal(cx, height);
JS_SetProperty(cx, obj, "type", &ptype);
JS_SetProperty(cx, obj, "width", &pwidth);
JS_SetProperty(cx, obj, "depth", &pdepth);
JS_SetProperty(cx, obj, "height", &pheight);
JS::RootedValue ptype(cx);
JS::RootedValue pwidth(cx);
JS::RootedValue pdepth(cx);
JS::RootedValue pheight(cx);
ScriptInterface::ToJSVal<std::string>(cx, ptype.get(), "square");
ScriptInterface::ToJSVal(cx, pwidth.get(), size0);
ScriptInterface::ToJSVal(cx, pdepth.get(), size1);
ScriptInterface::ToJSVal(cx, pheight.get(), 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());
}
return OBJECT_TO_JSVAL(obj);

View File

@ -34,7 +34,7 @@ public:
void test_basic()
{
ComponentTestHelper test;
ComponentTestHelper test(ScriptInterface::CreateRuntime());
std::vector<SimulationCommand> empty;

View File

@ -68,7 +68,7 @@ public:
ent3z = ent2z + ent2r + ent3r; // ensure it just touches the border of ent2
ent3g = ent3;
testHelper = new ComponentTestHelper;
testHelper = new ComponentTestHelper(ScriptInterface::CreateRuntime());
cmp = testHelper->Add<ICmpObstructionManager>(CID_ObstructionManager, "");
cmp->SetBounds(fixed::FromInt(0), fixed::FromInt(0), fixed::FromInt(1000), fixed::FromInt(1000));

View File

@ -39,7 +39,7 @@ public:
void test_basic()
{
ComponentTestHelper test;
ComponentTestHelper test(ScriptInterface::CreateRuntime());
MockTerrain terrain;
test.AddMock(SYSTEM_ENTITY, IID_Terrain, terrain);
@ -111,7 +111,7 @@ public:
void test_serialize()
{
ComponentTestHelper test;
ComponentTestHelper test(ScriptInterface::CreateRuntime());
MockTerrain terrain;
test.AddMock(SYSTEM_ENTITY, IID_Terrain, terrain);

View File

@ -77,7 +77,7 @@ public:
void test_basic()
{
ComponentTestHelper test;
ComponentTestHelper test(ScriptInterface::CreateRuntime());
ICmpRangeManager* cmp = test.Add<ICmpRangeManager>(CID_RangeManager, "");

View File

@ -32,15 +32,22 @@
#define FAIL(msg) STMT(JS_ReportError(cx, msg); return false)
template<> jsval ScriptInterface::ToJSVal<IComponent*>(JSContext* cx, IComponent* const& val)
template<> void ScriptInterface::ToJSVal<IComponent*>(JSContext* cx, JS::Value& ret, IComponent* const& val)
{
JSAutoRequest rq(cx);
if (val == NULL)
return JSVAL_NULL;
{
ret = JS::NullValue();
return;
}
// If this is a scripted component, just return the JS object directly
jsval instance = val->GetJSInstance();
if (!JSVAL_IS_NULL(instance))
return instance;
JS::RootedValue instance(cx, val->GetJSInstance());
if (!instance.isNull())
{
ret = instance;
return;
}
// Otherwise we need to construct a wrapper object
// (TODO: cache wrapper objects?)
@ -49,84 +56,97 @@ template<> jsval ScriptInterface::ToJSVal<IComponent*>(JSContext* cx, IComponent
{
// Report as an error, since scripts really shouldn't try to use unscriptable interfaces
LOGERROR(L"IComponent does not have a scriptable interface");
return JSVAL_VOID;
ret = JS::UndefinedValue();
return;
}
JSObject* obj = JS_NewObject(cx, cls, NULL, NULL);
JS::RootedObject obj(cx, JS_NewObject(cx, cls, NULL, NULL));
if (!obj)
{
LOGERROR(L"Failed to construct IComponent script object");
return JSVAL_VOID;
ret = JS::UndefinedValue();
return;
}
JS_SetPrivate(cx, obj, static_cast<void*>(val));
JS_SetPrivate(obj, static_cast<void*>(val));
return OBJECT_TO_JSVAL(obj);
ret = JS::ObjectValue(*obj);
}
template<> jsval ScriptInterface::ToJSVal<CParamNode>(JSContext* cx, CParamNode const& val)
template<> void ScriptInterface::ToJSVal<CParamNode>(JSContext* cx, JS::Value& ret, CParamNode const& val)
{
jsval rval = val.ToJSVal(cx, true);
JSAutoRequest rq(cx);
ret = val.ToJSVal(cx, true);
// Prevent modifications to the object, so that it's safe to share between
// components and to reconstruct on deserialization
if (JSVAL_IS_OBJECT(rval))
JS_DeepFreezeObject(cx, JSVAL_TO_OBJECT(rval));
return rval;
if (ret.isObject())
JS_DeepFreezeObject(cx, &ret.toObject());
}
template<> jsval ScriptInterface::ToJSVal<const CParamNode*>(JSContext* cx, const CParamNode* const& val)
template<> void ScriptInterface::ToJSVal<const CParamNode*>(JSContext* cx, JS::Value& ret, const CParamNode* const& val)
{
if (val)
return ToJSVal(cx, *val);
ToJSVal(cx, ret, *val);
else
return JSVAL_VOID;
ret = JSVAL_VOID;
}
template<> bool ScriptInterface::FromJSVal<CColor>(JSContext* cx, jsval v, CColor& out)
{
if (!JSVAL_IS_OBJECT(v))
if (!v.isObject())
FAIL("jsval not an object");
JSObject* obj = JSVAL_TO_OBJECT(v);
JSAutoRequest rq(cx);
JS::RootedObject obj(cx, &v.toObject());
jsval r, g, b, a;
if (!JS_GetProperty(cx, obj, "r", &r) || !FromJSVal(cx, r, out.r))
JS::RootedValue r(cx);
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))
FAIL("Failed to get property CColor.r");
if (!JS_GetProperty(cx, obj, "g", &g) || !FromJSVal(cx, g, out.g))
if (!JS_GetProperty(cx, obj, "g", g.address()) || !FromJSVal(cx, g, out.g))
FAIL("Failed to get property CColor.g");
if (!JS_GetProperty(cx, obj, "b", &b) || !FromJSVal(cx, b, out.b))
if (!JS_GetProperty(cx, obj, "b", b.address()) || !FromJSVal(cx, b, out.b))
FAIL("Failed to get property CColor.b");
if (!JS_GetProperty(cx, obj, "a", &a) || !FromJSVal(cx, a, out.a))
if (!JS_GetProperty(cx, obj, "a", a.address()) || !FromJSVal(cx, a, out.a))
FAIL("Failed to get property CColor.a");
// TODO: this probably has GC bugs if a getter returns an unrooted value
return true;
}
template<> jsval ScriptInterface::ToJSVal<CColor>(JSContext* cx, CColor const& val)
template<> void ScriptInterface::ToJSVal<CColor>(JSContext* cx, JS::Value& ret, CColor const& val)
{
JSObject* obj = JS_NewObject(cx, NULL, NULL, NULL);
JSAutoRequest rq(cx);
JS::RootedObject obj(cx, JS_NewObject(cx, NULL, NULL, NULL));
if (!obj)
return JSVAL_VOID;
{
ret = JSVAL_VOID;
return;
}
jsval r = ToJSVal(cx, val.r);
jsval g = ToJSVal(cx, val.g);
jsval b = ToJSVal(cx, val.b);
jsval a = ToJSVal(cx, val.a);
JS::RootedValue r(cx);
JS::RootedValue g(cx);
JS::RootedValue b(cx);
JS::RootedValue a(cx);
ToJSVal(cx, r.get(), val.r);
ToJSVal(cx, g.get(), val.g);
ToJSVal(cx, b.get(), val.b);
ToJSVal(cx, a.get(), val.a);
JS_SetProperty(cx, obj, "r", &r);
JS_SetProperty(cx, obj, "g", &g);
JS_SetProperty(cx, obj, "b", &b);
JS_SetProperty(cx, obj, "a", &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());
return OBJECT_TO_JSVAL(obj);
ret = JS::ObjectValue(*obj);
}
template<> bool ScriptInterface::FromJSVal<fixed>(JSContext* cx, jsval v, fixed& out)
{
jsdouble ret;
if (!JS_ValueToNumber(cx, v, &ret))
JSAutoRequest rq(cx);
double ret;
if (!JS::ToNumber(cx, v, &ret))
return false;
out = fixed::FromDouble(ret);
// double can precisely represent the full range of fixed, so this is a non-lossy conversion
@ -134,121 +154,146 @@ template<> bool ScriptInterface::FromJSVal<fixed>(JSContext* cx, jsval v, fixed&
return true;
}
template<> jsval ScriptInterface::ToJSVal<fixed>(JSContext* cx, const fixed& val)
template<> void ScriptInterface::ToJSVal<fixed>(JSContext* UNUSED(cx), JS::Value& ret, const fixed& val)
{
jsval rval = JSVAL_VOID;
JS_NewNumberValue(cx, val.ToDouble(), &rval); // ignore return value
return rval;
ret = JS::NumberValue(val.ToDouble());
}
template<> bool ScriptInterface::FromJSVal<CFixedVector3D>(JSContext* cx, jsval v, CFixedVector3D& out)
{
if (!JSVAL_IS_OBJECT(v))
if (!v.isObject())
return false; // TODO: report type error
JSObject* obj = JSVAL_TO_OBJECT(v);
jsval p;
JSAutoRequest rq(cx);
JS::RootedObject obj(cx, &v.toObject());
JS::RootedValue p(cx);
if (!JS_GetProperty(cx, obj, "x", &p)) return false; // TODO: report type errors
if (!JS_GetProperty(cx, obj, "x", p.address())) return false; // TODO: report type errors
if (!FromJSVal(cx, p, out.X)) return false;
if (!JS_GetProperty(cx, obj, "y", &p)) return false;
if (!JS_GetProperty(cx, obj, "y", p.address())) return false;
if (!FromJSVal(cx, p, out.Y)) return false;
if (!JS_GetProperty(cx, obj, "z", &p)) return false;
if (!JS_GetProperty(cx, obj, "z", p.address())) return false;
if (!FromJSVal(cx, p, out.Z)) return false;
return true;
}
template<> jsval ScriptInterface::ToJSVal<CFixedVector3D>(JSContext* cx, const CFixedVector3D& val)
template<> void ScriptInterface::ToJSVal<CFixedVector3D>(JSContext* cx, JS::Value& ret, const CFixedVector3D& val)
{
JSAutoRequest rq(cx);
// apply the Vector3D prototype to the return value;
ScriptInterface::CxPrivate* pCxPrivate = ScriptInterface::GetScriptInterfaceAndCBData(cx);
JSObject* obj = JS_NewObject(cx, NULL, JSVAL_TO_OBJECT(pCxPrivate->pScriptInterface->GetCachedValue(ScriptInterface::CACHE_VECTOR3DPROTO).get()), NULL);
JS::RootedObject obj(cx, JS_NewObject(cx, NULL,
&pCxPrivate->pScriptInterface->GetCachedValue(ScriptInterface::CACHE_VECTOR3DPROTO).get().toObject(),
NULL));
if (!obj)
return JSVAL_VOID;
{
ret = JSVAL_VOID;
return;
}
jsval x = ToJSVal(cx, val.X);
jsval y = ToJSVal(cx, val.Y);
jsval z = ToJSVal(cx, val.Z);
JS::RootedValue x(cx);
JS::RootedValue y(cx);
JS::RootedValue z(cx);
ToJSVal(cx, x.get(), val.X);
ToJSVal(cx, y.get(), val.Y);
ToJSVal(cx, z.get(), val.Z);
JS_SetProperty(cx, obj, "x", &x);
JS_SetProperty(cx, obj, "y", &y);
JS_SetProperty(cx, obj, "z", &z);
JS_SetProperty(cx, obj, "x", x.address());
JS_SetProperty(cx, obj, "y", y.address());
JS_SetProperty(cx, obj, "z", z.address());
return OBJECT_TO_JSVAL(obj);
ret = JS::ObjectValue(*obj);
}
template<> bool ScriptInterface::FromJSVal<CFixedVector2D>(JSContext* cx, jsval v, CFixedVector2D& out)
{
if (!JSVAL_IS_OBJECT(v))
JSAutoRequest rq(cx);
if (!v.isObject())
return false; // TODO: report type error
JSObject* obj = JSVAL_TO_OBJECT(v);
JS::RootedObject obj(cx, &v.toObject());
jsval p;
JS::RootedValue p(cx);
if (!JS_GetProperty(cx, obj, "x", &p)) return false; // TODO: report type errors
if (!JS_GetProperty(cx, obj, "x", p.address())) return false; // TODO: report type errors
if (!FromJSVal(cx, p, out.X)) return false;
if (!JS_GetProperty(cx, obj, "y", &p)) return false;
if (!JS_GetProperty(cx, obj, "y", p.address())) return false;
if (!FromJSVal(cx, p, out.Y)) return false;
return true;
}
template<> jsval ScriptInterface::ToJSVal<CFixedVector2D>(JSContext* cx, const CFixedVector2D& val)
template<> void ScriptInterface::ToJSVal<CFixedVector2D>(JSContext* cx, JS::Value& ret, const CFixedVector2D& val)
{
JSAutoRequest rq(cx);
// apply the Vector2D prototype to the return value
ScriptInterface::CxPrivate* pCxPrivate = ScriptInterface::GetScriptInterfaceAndCBData(cx);
JSObject* obj = JS_NewObject(cx, NULL, JSVAL_TO_OBJECT(pCxPrivate->pScriptInterface->GetCachedValue(ScriptInterface::CACHE_VECTOR2DPROTO).get()), NULL);
JS::RootedObject obj(cx, JS_NewObject(cx, NULL,
&pCxPrivate->pScriptInterface->GetCachedValue(ScriptInterface::CACHE_VECTOR2DPROTO).get().toObject(),
NULL));
if (!obj)
return JSVAL_VOID;
{
ret = JSVAL_VOID;
return;
}
jsval x = ToJSVal(cx, val.X);
jsval y = ToJSVal(cx, val.Y);
JS::RootedValue x(cx);
JS::RootedValue y(cx);
ToJSVal(cx, x.get(), val.X);
ToJSVal(cx, y.get(), val.Y);
JS_SetProperty(cx, obj, "x", &x);
JS_SetProperty(cx, obj, "y", &y);
JS_SetProperty(cx, obj, "x", x.address());
JS_SetProperty(cx, obj, "y", y.address());
return OBJECT_TO_JSVAL(obj);
ret = JS::ObjectValue(*obj);
}
template<jsint atype, typename T> jsval ToJSVal_Grid(JSContext* cx, const Grid<T>& val)
template<> void ScriptInterface::ToJSVal<Grid<u8> >(JSContext* cx, JS::Value& ret, const Grid<u8>& val)
{
JSObject* obj = JS_NewObject(cx, NULL, NULL, NULL);
if (!obj)
return JSVAL_VOID;
JSAutoRequest rq(cx);
u32 length = (u32)(val.m_W * val.m_H);
u32 nbytes = (u32)(length * sizeof(u8));
JS::RootedObject objArr(cx, JS_NewUint8Array(cx, length));
memcpy((void*)JS_GetUint8ArrayData(objArr), val.m_Data, nbytes);
jsuint len = val.m_W * val.m_H;
JSObject *darray = js_CreateTypedArray(cx, atype, len);
if (!darray)
return JSVAL_VOID;
JS::RootedValue data(cx, JS::ObjectValue(*objArr));
JS::RootedValue w(cx);
JS::RootedValue h(cx);
ScriptInterface::ToJSVal(cx, w.get(), val.m_W);
ScriptInterface::ToJSVal(cx, h.get(), val.m_H);
js::TypedArray *tdest = js::TypedArray::fromJSObject(darray);
ENSURE(tdest->byteLength == len*sizeof(T));
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());
memcpy(tdest->data, val.m_Data, tdest->byteLength);
jsval w = ScriptInterface::ToJSVal(cx, val.m_W);
jsval h = ScriptInterface::ToJSVal(cx, val.m_H);
jsval data = OBJECT_TO_JSVAL(darray);
JS_SetProperty(cx, obj, "width", &w);
JS_SetProperty(cx, obj, "height", &h);
JS_SetProperty(cx, obj, "data", &data);
return OBJECT_TO_JSVAL(obj);
ret = JS::ObjectValue(*obj);
}
template<> void ScriptInterface::ToJSVal<Grid<u16> >(JSContext* cx, JS::Value& ret, const Grid<u16>& val)
{
JSAutoRequest rq(cx);
u32 length = (u32)(val.m_W * val.m_H);
u32 nbytes = (u32)(length * sizeof(u16));
JS::RootedObject objArr(cx, JS_NewUint16Array(cx, length));
memcpy((void*)JS_GetUint16ArrayData(objArr), val.m_Data, nbytes);
JS::RootedValue data(cx, JS::ObjectValue(*objArr));
JS::RootedValue w(cx);
JS::RootedValue h(cx);
ScriptInterface::ToJSVal(cx, w.get(), val.m_W);
ScriptInterface::ToJSVal(cx, h.get(), val.m_H);
template<> jsval ScriptInterface::ToJSVal<Grid<u8> >(JSContext* cx, const Grid<u8>& val)
{
return ToJSVal_Grid<js::TypedArray::TYPE_UINT8>(cx, val);
}
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());
template<> jsval ScriptInterface::ToJSVal<Grid<u16> >(JSContext* cx, const Grid<u16>& val)
{
return ToJSVal_Grid<js::TypedArray::TYPE_UINT16>(cx, val);
ret = JS::ObjectValue(*obj);
}

View File

@ -21,32 +21,40 @@
#include "scriptinterface/ScriptInterface.h"
#include "simulation2/MessageTypes.h"
#include "js/jsapi.h"
#define TOJSVAL_SETUP() \
JSObject* obj = JS_NewObject(scriptInterface.GetContext(), NULL, NULL, NULL); \
if (! obj) \
return JSVAL_VOID;
JSObject* obj; \
{\
JSAutoRequest rq(scriptInterface.GetContext()); \
obj = JS_NewObject(scriptInterface.GetContext(), NULL, NULL, NULL); \
if (!obj) \
return JSVAL_VOID; \
}
#define SET_MSG_PROPERTY(name) \
do { \
jsval prop = ScriptInterface::ToJSVal(scriptInterface.GetContext(), this->name); \
if (! JS_SetProperty(scriptInterface.GetContext(), obj, #name, &prop)) \
JSAutoRequest rq(scriptInterface.GetContext()); \
JSContext* cx = scriptInterface.GetContext(); \
JS::RootedValue prop(cx);\
ScriptInterface::ToJSVal(cx, prop.get(), this->name); \
if (! JS_SetProperty(cx, obj, #name, prop.address())) \
return JSVAL_VOID; \
} while (0);
#define FROMJSVAL_SETUP() \
if (! JSVAL_IS_OBJECT(val)) \
if ( JSVAL_IS_PRIMITIVE(val)) \
return NULL; \
JSObject* obj = JSVAL_TO_OBJECT(val); \
jsval prop;
JS::RootedValue prop(scriptInterface.GetContext());
#define GET_MSG_PROPERTY(type, name) \
if (! JS_GetProperty(scriptInterface.GetContext(), obj, #name, &prop)) \
return NULL; \
type name; \
if (! ScriptInterface::FromJSVal(scriptInterface.GetContext(), prop, name)) \
return NULL;
{ \
JSAutoRequest rq(scriptInterface.GetContext()); \
if (! JS_GetProperty(scriptInterface.GetContext(), obj, #name, prop.address())) \
return NULL; \
if (! ScriptInterface::FromJSVal(scriptInterface.GetContext(), prop.get(), name)) \
return NULL; \
}
jsval CMessage::ToJSValCached(ScriptInterface& scriptInterface) const
{

View File

@ -19,35 +19,34 @@
#include "BinarySerializer.h"
#include "SerializedScriptTypes.h"
#include "lib/alignment.h"
#include "ps/CLogger.h"
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/ScriptExtraHeaders.h" // for JSDOUBLE_IS_INT32, typed arrays
#include "SerializedScriptTypes.h"
static u8 GetArrayType(uint32 arrayType)
static u8 GetArrayType(JSArrayBufferViewType arrayType)
{
switch(arrayType)
{
case js::TypedArray::TYPE_INT8:
case js::ArrayBufferView::TYPE_INT8:
return SCRIPT_TYPED_ARRAY_INT8;
case js::TypedArray::TYPE_UINT8:
case js::ArrayBufferView::TYPE_UINT8:
return SCRIPT_TYPED_ARRAY_UINT8;
case js::TypedArray::TYPE_INT16:
case js::ArrayBufferView::TYPE_INT16:
return SCRIPT_TYPED_ARRAY_INT16;
case js::TypedArray::TYPE_UINT16:
case js::ArrayBufferView::TYPE_UINT16:
return SCRIPT_TYPED_ARRAY_UINT16;
case js::TypedArray::TYPE_INT32:
case js::ArrayBufferView::TYPE_INT32:
return SCRIPT_TYPED_ARRAY_INT32;
case js::TypedArray::TYPE_UINT32:
case js::ArrayBufferView::TYPE_UINT32:
return SCRIPT_TYPED_ARRAY_UINT32;
case js::TypedArray::TYPE_FLOAT32:
case js::ArrayBufferView::TYPE_FLOAT32:
return SCRIPT_TYPED_ARRAY_FLOAT32;
case js::TypedArray::TYPE_FLOAT64:
case js::ArrayBufferView::TYPE_FLOAT64:
return SCRIPT_TYPED_ARRAY_FLOAT64;
case js::TypedArray::TYPE_UINT8_CLAMPED:
case js::ArrayBufferView::TYPE_UINT8_CLAMPED:
return SCRIPT_TYPED_ARRAY_UINT8_CLAMPED;
default:
LOGERROR(L"Cannot serialize unrecognized typed array view: %d", arrayType);
@ -64,6 +63,7 @@ CBinarySerializerScriptImpl::CBinarySerializerScriptImpl(ScriptInterface& script
void CBinarySerializerScriptImpl::HandleScriptVal(jsval val)
{
JSContext* cx = m_ScriptInterface.GetContext();
JSAutoRequest rq(cx);
switch (JS_TypeOfValue(cx, val))
{
@ -79,13 +79,13 @@ void CBinarySerializerScriptImpl::HandleScriptVal(jsval val)
}
case JSTYPE_OBJECT:
{
if (JSVAL_IS_NULL(val))
if (val.isNull())
{
m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_NULL);
break;
}
JSObject* obj = JSVAL_TO_OBJECT(val);
JS::RootedObject obj(cx, &val.toObject());
// If we've already serialized this object, just output a reference to it
u32 tag = GetScriptBackrefTag(obj);
@ -104,53 +104,50 @@ void CBinarySerializerScriptImpl::HandleScriptVal(jsval val)
// Arrays like [1, 2, ] have an 'undefined' at the end which is part of the
// length but seemingly isn't enumerated, so store the length explicitly
jsuint length = 0;
uint length = 0;
if (!JS_GetArrayLength(cx, obj, &length))
throw PSERROR_Serialize_ScriptError("JS_GetArrayLength failed");
m_Serializer.NumberU32_Unbounded("array length", length);
}
else if (js_IsTypedArray(obj))
else if (JS_IsTypedArrayObject(obj))
{
m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_TYPED_ARRAY);
js::TypedArray* typedArray = js::TypedArray::fromJSObject(obj);
m_Serializer.NumberU8_Unbounded("array type", GetArrayType(typedArray->type));
m_Serializer.NumberU32_Unbounded("byte offset", typedArray->byteOffset);
m_Serializer.NumberU32_Unbounded("length", typedArray->length);
m_Serializer.NumberU8_Unbounded("array type", GetArrayType(JS_GetArrayBufferViewType(obj)));
m_Serializer.NumberU32_Unbounded("byte offset", JS_GetTypedArrayByteOffset(obj));
m_Serializer.NumberU32_Unbounded("length", JS_GetTypedArrayLength(obj));
// Now handle its array buffer
// this may be a backref, since ArrayBuffers can be shared by multiple views
HandleScriptVal(OBJECT_TO_JSVAL(typedArray->bufferJS));
HandleScriptVal(JS::ObjectValue(*JS_GetArrayBufferViewBuffer(obj)));
break;
}
else if (js_IsArrayBuffer(obj))
else if (JS_IsArrayBufferObject(obj))
{
m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_ARRAY_BUFFER);
js::ArrayBuffer* arrayBuffer = js::ArrayBuffer::fromJSObject(obj);
#if BYTE_ORDER != LITTLE_ENDIAN
#error TODO: need to convert JS ArrayBuffer data to little-endian
#endif
u32 length = arrayBuffer->byteLength;
u32 length = JS_GetArrayBufferByteLength(obj);
m_Serializer.NumberU32_Unbounded("buffer length", length);
m_Serializer.RawBytes("buffer data", (const u8*)arrayBuffer->data, length);
m_Serializer.RawBytes("buffer data", (const u8*)JS_GetArrayBufferData(obj), length);
break;
}
else
{
// Find type of object
JSClass* jsclass = JS_GET_CLASS(cx, obj);
JSClass* jsclass = JS_GetClass(obj);
if (!jsclass)
throw PSERROR_Serialize_ScriptError("JS_GET_CLASS failed");
throw PSERROR_Serialize_ScriptError("JS_GetClass failed");
JSProtoKey protokey = JSCLASS_CACHED_PROTO_KEY(jsclass);
if (protokey == JSProto_Object)
{
// Object class - check for user-defined prototype
JSObject* proto = JS_GetPrototype(cx, obj);
JS::RootedObject proto(cx);
JS_GetPrototype(cx, obj, proto.address());
if (!proto)
throw PSERROR_Serialize_ScriptError("JS_GetPrototype failed");
@ -175,15 +172,15 @@ void CBinarySerializerScriptImpl::HandleScriptVal(jsval val)
JSBool hasCustomSerialize;
if (!JS_HasProperty(cx, obj, "Serialize", &hasCustomSerialize))
throw PSERROR_Serialize_ScriptError("JS_HasProperty failed");
if (hasCustomSerialize)
{
jsval serialize;
if (!JS_LookupProperty(cx, obj, "Serialize", &serialize))
JS::RootedValue serialize(cx);
if (!JS_LookupProperty(cx, obj, "Serialize", serialize.address()))
throw PSERROR_Serialize_ScriptError("JS_LookupProperty failed");
// If serialize is null, so don't serialize anything more
if (!JSVAL_IS_NULL(serialize))
if (!serialize.isNull())
{
CScriptValRooted data;
if (!m_ScriptInterface.CallFunction(val, "Serialize", data))
@ -199,9 +196,9 @@ void CBinarySerializerScriptImpl::HandleScriptVal(jsval val)
// Standard Number object
m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_OBJECT_NUMBER);
// Get primitive value
jsdouble d;
if (!JS_ValueToNumber(cx, val, &d))
throw PSERROR_Serialize_ScriptError("JS_ValueToNumber failed");
double d;
if (!JS::ToNumber(cx, val, &d))
throw PSERROR_Serialize_ScriptError("JS::ToNumber failed");
m_Serializer.NumberDouble_Unbounded("value", d);
break;
}
@ -221,10 +218,8 @@ void CBinarySerializerScriptImpl::HandleScriptVal(jsval val)
// Standard Boolean object
m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_OBJECT_BOOLEAN);
// Get primitive value
JSBool b;
if (!JS_ValueToBoolean(cx, val, &b))
throw PSERROR_Serialize_ScriptError("JS_ValueToBoolean failed");
m_Serializer.Bool("value", b == JS_TRUE);
bool b = JS::ToBoolean(val);
m_Serializer.Bool("value", b);
break;
}
else
@ -240,22 +235,23 @@ void CBinarySerializerScriptImpl::HandleScriptVal(jsval val)
// (Note that we don't do any rooting, because we assume nothing is going to trigger GC.
// I'm not absolute certain that's necessarily a valid assumption.)
AutoJSIdArray ida (cx, JS_Enumerate(cx, obj));
if (!ida.get())
JS::AutoIdArray ida (cx, JS_Enumerate(cx, obj));
if (!ida)
throw PSERROR_Serialize_ScriptError("JS_Enumerate failed");
m_Serializer.NumberU32_Unbounded("num props", (uint32_t)ida.length());
m_Serializer.NumberU32_Unbounded("num props", (u32)ida.length());
for (size_t i = 0; i < ida.length(); ++i)
{
jsid id = ida[i];
jsval idval, propval;
JS::RootedValue idval(cx);
JS::RootedValue propval(cx);
// Get the property name as a string
if (!JS_IdToValue(cx, id, &idval))
if (!JS_IdToValue(cx, id, idval.address()))
throw PSERROR_Serialize_ScriptError("JS_IdToValue failed");
JSString* idstr = JS_ValueToString(cx, idval);
JSString* idstr = JS_ValueToString(cx, idval.get());
if (!idstr)
throw PSERROR_Serialize_ScriptError("JS_ValueToString failed");
@ -263,7 +259,7 @@ void CBinarySerializerScriptImpl::HandleScriptVal(jsval 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))
if (!JS_LookupPropertyById(cx, obj, id, propval.address()))
throw PSERROR_Serialize_ScriptError("JS_LookupPropertyById failed");
HandleScriptVal(propval);
@ -294,50 +290,40 @@ void CBinarySerializerScriptImpl::HandleScriptVal(jsval val)
case JSTYPE_STRING:
{
m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_STRING);
ScriptString("string", JSVAL_TO_STRING(val));
ScriptString("string", val.toString());
break;
}
case JSTYPE_NUMBER:
{
// For efficiency, handle ints and doubles separately.
if (JSVAL_IS_INT(val))
// To reduce the size of the serialized data, we handle integers and doubles separately.
// We can't check for val.isInt32 and val.isDouble directly, because integer numbers are not guaranteed
// to be represented as integers. A number like 33 could be stored as integer on the computer of one player
// and as double on the other player's computer. That would cause out of sync errors in multiplayer games because
// their binary representation and thus the hash would be different.
double d;
d = val.toNumber();
i32 integer;
if (JS_DoubleIsInt32(d, &integer))
{
m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_INT);
m_Serializer.NumberI32_Unbounded("value", (int32_t)JSVAL_TO_INT(val));
m_Serializer.NumberI32_Unbounded("value", integer);
}
else
{
ENSURE(JSVAL_IS_DOUBLE(val));
// If the value fits in an int, serialise as an int
jsdouble d = JSVAL_TO_DOUBLE(val);
int32_t i;
if (JSDOUBLE_IS_INT32(d, &i))
{
m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_INT);
m_Serializer.NumberI32_Unbounded("value", i);
}
// Otherwise serialise as a double
else
{
m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_DOUBLE);
m_Serializer.NumberDouble_Unbounded("value", d);
}
m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_DOUBLE);
m_Serializer.NumberDouble_Unbounded("value", d);
}
break;
}
case JSTYPE_BOOLEAN:
{
m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_BOOLEAN);
JSBool b = JSVAL_TO_BOOLEAN(val);
bool b = JSVAL_TO_BOOLEAN(val);
m_Serializer.NumberU8_Unbounded("value", b ? 1 : 0);
break;
}
case JSTYPE_XML:
{
LOGERROR(L"Cannot serialise JS objects of type 'xml'");
throw PSERROR_Serialize_InvalidScriptValue();
}
default:
{
debug_warn(L"Invalid TypeOfValue");
@ -349,6 +335,8 @@ void CBinarySerializerScriptImpl::HandleScriptVal(jsval val)
void CBinarySerializerScriptImpl::ScriptString(const char* name, JSString* string)
{
JSContext* cx = m_ScriptInterface.GetContext();
JSAutoRequest rq(cx);
size_t length;
const jschar* chars = JS_GetStringCharsAndLength(cx, string, &length);
@ -360,7 +348,7 @@ void CBinarySerializerScriptImpl::ScriptString(const char* name, JSString* strin
#endif
// Serialize strings directly as UTF-16, to avoid expensive encoding conversions
m_Serializer.NumberU32_Unbounded("string length", (uint32_t)length);
m_Serializer.NumberU32_Unbounded("string length", (u32)length);
m_Serializer.RawBytes(name, (const u8*)chars, length*2);
}

View File

@ -25,37 +25,8 @@
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/ScriptExtraHeaders.h" // for typed arrays
#include "js/jsapi.h"
#include "lib/byte_order.h"
static uint32 GetJSArrayType(u8 arrayType)
{
switch(arrayType)
{
case SCRIPT_TYPED_ARRAY_INT8:
return js::TypedArray::TYPE_INT8;
case SCRIPT_TYPED_ARRAY_UINT8:
return js::TypedArray::TYPE_UINT8;
case SCRIPT_TYPED_ARRAY_INT16:
return js::TypedArray::TYPE_INT16;
case SCRIPT_TYPED_ARRAY_UINT16:
return js::TypedArray::TYPE_UINT16;
case SCRIPT_TYPED_ARRAY_INT32:
return js::TypedArray::TYPE_INT32;
case SCRIPT_TYPED_ARRAY_UINT32:
return js::TypedArray::TYPE_UINT32;
case SCRIPT_TYPED_ARRAY_FLOAT32:
return js::TypedArray::TYPE_FLOAT32;
case SCRIPT_TYPED_ARRAY_FLOAT64:
return js::TypedArray::TYPE_FLOAT64;
case SCRIPT_TYPED_ARRAY_UINT8_CLAMPED:
return js::TypedArray::TYPE_UINT8_CLAMPED;
default:
throw PSERROR_Deserialize_ScriptError("Failed to deserialize unrecognized typed array view");
}
}
CStdDeserializer::CStdDeserializer(ScriptInterface& scriptInterface, std::istream& stream) :
m_ScriptInterface(scriptInterface), m_Stream(stream)
{
@ -138,11 +109,13 @@ void CStdDeserializer::SetReservedScriptBackref(u32 tag, 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)
{
if (!JS_RemoveObjectRoot(m_ScriptInterface.GetContext(), &it->second))
throw PSERROR_Deserialize_ScriptError("JS_RemoveRoot failed");
JS_RemoveObjectRoot(m_ScriptInterface.GetContext(), &it->second);
}
m_ScriptBackrefs.clear();
}
@ -153,6 +126,8 @@ jsval CStdDeserializer::ReadScriptVal(const char* UNUSED(name), JSObject* append
{
JSContext* cx = m_ScriptInterface.GetContext();
JSAutoRequest rq(cx);
uint8_t type;
NumberU8_Unbounded("type", type);
switch (type)
@ -192,25 +167,25 @@ jsval CStdDeserializer::ReadScriptVal(const char* UNUSED(name), JSObject* append
if (!proto)
throw PSERROR_Deserialize_ScriptError("Failed to find serializable prototype for object");
JSObject* parent = JS_GetParent(cx, proto);
JSObject* parent = JS_GetParent(proto);
if (!proto || !parent)
throw PSERROR_Deserialize_ScriptError();
obj = JS_NewObject(cx, NULL, proto, parent);
if (!obj)
throw PSERROR_Deserialize_ScriptError("JS_NewObject failed");
CScriptValRooted objRoot(cx, OBJECT_TO_JSVAL(obj));
CScriptValRooted objRoot(cx, JS::ObjectValue(*obj));
// Does it have custom Deserialize function?
// if so, we let it handle the deserialized data, rather than adding properties directly
JSBool hasCustomDeserialize, hasCustomSerialize;
if (!JS_HasProperty(cx, obj, "Serialize", &hasCustomSerialize) || !JS_HasProperty(cx, obj, "Deserialize", &hasCustomDeserialize))
throw PSERROR_Serialize_ScriptError("JS_HasProperty failed");
if (hasCustomDeserialize)
{
jsval serialize;
if (!JS_LookupProperty(cx, obj, "Serialize", &serialize))
JS::RootedValue serialize(cx);
if (!JS_LookupProperty(cx, obj, "Serialize", serialize.address()))
throw PSERROR_Serialize_ScriptError("JS_LookupProperty failed");
bool hasNullSerialize = hasCustomSerialize && JSVAL_IS_NULL(serialize);
@ -219,17 +194,17 @@ jsval CStdDeserializer::ReadScriptVal(const char* UNUSED(name), JSObject* append
if (!hasNullSerialize)
ScriptVal("data", data);
m_ScriptInterface.CallFunctionVoid(OBJECT_TO_JSVAL(obj), "Deserialize", data);
m_ScriptInterface.CallFunctionVoid(JS::ObjectValue(*obj), "Deserialize", data);
AddScriptBackref(obj);
return OBJECT_TO_JSVAL(obj);
return JS::ObjectValue(*obj);
}
}
if (!obj)
throw PSERROR_Deserialize_ScriptError("Deserializer failed to create new object");
CScriptValRooted objRoot(cx, OBJECT_TO_JSVAL(obj));
CScriptValRooted objRoot(cx, JS::ObjectValue(*obj));
AddScriptBackref(obj);
@ -241,33 +216,33 @@ jsval CStdDeserializer::ReadScriptVal(const char* UNUSED(name), JSObject* append
utf16string propname;
ReadStringUTF16("prop name", propname);
jsval propval = ReadScriptVal("prop value", NULL);
JS::RootedValue propval(cx, ReadScriptVal("prop value", NULL));
CScriptValRooted propvalRoot(cx, propval);
if (!JS_SetUCProperty(cx, obj, (const jschar*)propname.data(), propname.length(), &propval))
if (!JS_SetUCProperty(cx, obj, (const jschar*)propname.data(), propname.length(), propval.address()))
throw PSERROR_Deserialize_ScriptError();
}
return OBJECT_TO_JSVAL(obj);
return JS::ObjectValue(*obj);
}
case SCRIPT_TYPE_STRING:
{
JSString* str;
ScriptString("string", str);
return STRING_TO_JSVAL(str);
return JS::StringValue(str);
}
case SCRIPT_TYPE_INT:
{
int32_t value;
NumberI32("value", value, JSVAL_INT_MIN, JSVAL_INT_MAX);
return INT_TO_JSVAL(value);
return JS::NumberValue(value);
}
case SCRIPT_TYPE_DOUBLE:
{
double value;
NumberDouble_Unbounded("value", value);
jsval rval;
if (!JS_NewNumberValue(cx, value, &rval))
jsval rval = JS::NumberValue(value);
if (JSVAL_IS_NULL(rval))
throw PSERROR_Deserialize_ScriptError("JS_NewNumberValue failed");
return rval;
}
@ -275,7 +250,7 @@ jsval CStdDeserializer::ReadScriptVal(const char* UNUSED(name), JSObject* append
{
uint8_t value;
NumberU8("value", value, 0, 1);
return BOOLEAN_TO_JSVAL(value ? JS_TRUE : JS_FALSE);
return JS::BooleanValue(value ? true : false);
}
case SCRIPT_TYPE_BACKREF:
{
@ -284,26 +259,24 @@ jsval CStdDeserializer::ReadScriptVal(const char* UNUSED(name), JSObject* append
JSObject* obj = GetScriptBackref(tag);
if (!obj)
throw PSERROR_Deserialize_ScriptError("Invalid backref tag");
return OBJECT_TO_JSVAL(obj);
return JS::ObjectValue(*obj);
}
case SCRIPT_TYPE_OBJECT_NUMBER:
{
double value;
NumberDouble_Unbounded("value", value);
jsval val;
if (!JS_NewNumberValue(cx, value, &val))
throw PSERROR_Deserialize_ScriptError();
JS::RootedValue val(cx, JS::NumberValue(value));
CScriptValRooted objRoot(cx, val);
JSObject* ctorobj;
if (!JS_GetClassObject(cx, JS_GetGlobalObject(cx), JSProto_Number, &ctorobj))
if (!JS_GetClassObject(cx, JS_GetGlobalForScopeChain(cx), JSProto_Number, &ctorobj))
throw PSERROR_Deserialize_ScriptError("JS_GetClassObject failed");
JSObject* obj = JS_New(cx, ctorobj, 1, &val);
JSObject* obj = JS_New(cx, ctorobj, 1, val.address());
if (!obj)
throw PSERROR_Deserialize_ScriptError("JS_New failed");
AddScriptBackref(obj);
return OBJECT_TO_JSVAL(obj);
return JS::ObjectValue(*obj);
}
case SCRIPT_TYPE_OBJECT_STRING:
{
@ -311,35 +284,34 @@ jsval CStdDeserializer::ReadScriptVal(const char* UNUSED(name), JSObject* append
ScriptString("value", str);
if (!str)
throw PSERROR_Deserialize_ScriptError();
jsval val = STRING_TO_JSVAL(str);
JS::RootedValue val(cx, JS::StringValue(str));
CScriptValRooted valRoot(cx, val);
JSObject* ctorobj;
if (!JS_GetClassObject(cx, JS_GetGlobalObject(cx), JSProto_String, &ctorobj))
if (!JS_GetClassObject(cx, JS_GetGlobalForScopeChain(cx), JSProto_String, &ctorobj))
throw PSERROR_Deserialize_ScriptError("JS_GetClassObject failed");
JSObject* obj = JS_New(cx, ctorobj, 1, &val);
JSObject* obj = JS_New(cx, ctorobj, 1, val.address());
if (!obj)
throw PSERROR_Deserialize_ScriptError("JS_New failed");
AddScriptBackref(obj);
return OBJECT_TO_JSVAL(obj);
return JS::ObjectValue(*obj);
}
case SCRIPT_TYPE_OBJECT_BOOLEAN:
{
bool value;
Bool("value", value);
jsval val = BOOLEAN_TO_JSVAL(value ? JS_TRUE : JS_FALSE);
CScriptValRooted objRoot(cx, val);
JS::RootedValue val(cx, JS::BooleanValue(value));
JSObject* ctorobj;
if (!JS_GetClassObject(cx, JS_GetGlobalObject(cx), JSProto_Boolean, &ctorobj))
if (!JS_GetClassObject(cx, JS_GetGlobalForScopeChain(cx), JSProto_Boolean, &ctorobj))
throw PSERROR_Deserialize_ScriptError("JS_GetClassObject failed");
JSObject* obj = JS_New(cx, ctorobj, 1, &val);
JSObject* obj = JS_New(cx, ctorobj, 1, val.address());
if (!obj)
throw PSERROR_Deserialize_ScriptError("JS_New failed");
AddScriptBackref(obj);
return OBJECT_TO_JSVAL(obj);
return JS::ObjectValue(*obj);
}
case SCRIPT_TYPE_TYPED_ARRAY:
{
@ -354,46 +326,71 @@ jsval CStdDeserializer::ReadScriptVal(const char* UNUSED(name), JSObject* append
// Get buffer object
jsval bufferVal = ReadScriptVal("buffer", NULL);
CScriptValRooted bufferValRoot(cx, bufferVal);
if (!JSVAL_IS_OBJECT(bufferVal))
if (!bufferVal.isObject())
throw PSERROR_Deserialize_ScriptError();
JSObject* bufferObj = JSVAL_TO_OBJECT(bufferVal);
if (!js_IsArrayBuffer(bufferObj))
JSObject* bufferObj = &bufferVal.toObject();
if (!JS_IsArrayBufferObject(bufferObj))
throw PSERROR_Deserialize_ScriptError("js_IsArrayBuffer failed");
JSObject* arrayObj = js_CreateTypedArrayWithBuffer(cx, GetJSArrayType(arrayType), bufferObj, byteOffset, length);
JSObject* arrayObj;
switch(arrayType)
{
case SCRIPT_TYPED_ARRAY_INT8:
arrayObj = JS_NewInt8ArrayWithBuffer(cx, bufferObj, byteOffset, length);
break;
case SCRIPT_TYPED_ARRAY_UINT8:
arrayObj = JS_NewUint8ArrayWithBuffer(cx, bufferObj, byteOffset, length);
break;
case SCRIPT_TYPED_ARRAY_INT16:
arrayObj = JS_NewInt16ArrayWithBuffer(cx, bufferObj, byteOffset, length);
break;
case SCRIPT_TYPED_ARRAY_UINT16:
arrayObj = JS_NewUint16ArrayWithBuffer(cx, bufferObj, byteOffset, length);
break;
case SCRIPT_TYPED_ARRAY_INT32:
arrayObj = JS_NewInt32ArrayWithBuffer(cx, bufferObj, byteOffset, length);
break;
case SCRIPT_TYPED_ARRAY_UINT32:
arrayObj = JS_NewUint32ArrayWithBuffer(cx, bufferObj, byteOffset, length);
break;
case SCRIPT_TYPED_ARRAY_FLOAT32:
arrayObj = JS_NewFloat32ArrayWithBuffer(cx, bufferObj, byteOffset, length);
break;
case SCRIPT_TYPED_ARRAY_FLOAT64:
arrayObj = JS_NewFloat64ArrayWithBuffer(cx, bufferObj, byteOffset, length);
break;
case SCRIPT_TYPED_ARRAY_UINT8_CLAMPED:
arrayObj = JS_NewUint8ClampedArrayWithBuffer(cx, bufferObj, byteOffset, length);
break;
default:
throw PSERROR_Deserialize_ScriptError("Failed to deserialize unrecognized typed array view");
}
if (!arrayObj)
throw PSERROR_Deserialize_ScriptError("js_CreateTypedArrayWithBuffer failed");
SetReservedScriptBackref(arrayTag, arrayObj);
return OBJECT_TO_JSVAL(arrayObj);
return JS::ObjectValue(*arrayObj);
}
case SCRIPT_TYPE_ARRAY_BUFFER:
{
u32 length;
NumberU32_Unbounded("buffer length", length);
u8* bufferData = NULL;
u8* bufferData = new u8[length];
RawBytes("buffer data", bufferData, length);
#if BYTE_ORDER != LITTLE_ENDIAN
#error TODO: need to convert JS ArrayBuffer data from little-endian
#endif
JSObject* bufferObj = js_CreateArrayBuffer(cx, length);
if (!bufferObj)
throw PSERROR_Deserialize_ScriptError("js_CreateArrayBuffer failed");
void* contents = NULL;
JS_AllocateArrayBufferContents(cx, length, &contents, &bufferData);
RawBytes("buffer data", bufferData, length);
JSObject* bufferObj = JS_NewArrayBufferWithContents(cx, contents);
AddScriptBackref(bufferObj);
js::ArrayBuffer* buffer = js::ArrayBuffer::fromJSObject(bufferObj);
memcpy(buffer->data, bufferData, length);
delete[] bufferData;
return OBJECT_TO_JSVAL(bufferObj);
return JS::ObjectValue(*bufferObj);
}
default:
throw PSERROR_Deserialize_OutOfBounds();
@ -440,7 +437,7 @@ void CStdDeserializer::ScriptVal(const char* name, CScriptValRooted& out)
void CStdDeserializer::ScriptObjectAppend(const char* name, jsval& obj)
{
if (!JSVAL_IS_OBJECT(obj))
if (!obj.isObject())
throw PSERROR_Deserialize_ScriptError();
ReadScriptVal(name, JSVAL_TO_OBJECT(obj));

View File

@ -146,6 +146,8 @@ bool CComponentManager::LoadScript(const VfsPath& filename, bool hotload)
void CComponentManager::Script_RegisterComponentType(ScriptInterface::CxPrivate* pCxPrivate, int iid, std::string cname, CScriptVal ctor)
{
CComponentManager* componentManager = static_cast<CComponentManager*> (pCxPrivate->pCBData);
JSContext* cx = componentManager->m_ScriptInterface.GetContext();
JSAutoRequest rq(cx);
// Find the C++ component that wraps the interface
int cidWrapper = componentManager->GetScriptWrapper(iid);
@ -234,7 +236,7 @@ void CComponentManager::Script_RegisterComponentType(ScriptInterface::CxPrivate*
ctWrapper.dealloc,
cname,
schema,
CScriptValRooted(componentManager->m_ScriptInterface.GetContext(), ctor)
CScriptValRooted(cx, ctor)
};
componentManager->m_ComponentTypesById[cid] = ct;
@ -242,13 +244,18 @@ void CComponentManager::Script_RegisterComponentType(ScriptInterface::CxPrivate*
// Find all the ctor prototype's On* methods, and subscribe to the appropriate messages:
CScriptVal proto;
if (!componentManager->m_ScriptInterface.GetProperty(ctor.get(), "prototype", proto))
JS::RootedValue protoVal(cx);
if (!componentManager->m_ScriptInterface.GetPropertyJS(ctor.get(), "prototype", &protoVal))
return; // error
std::vector<std::string> methods;
if (!componentManager->m_ScriptInterface.EnumeratePropertyNamesWithPrefix(proto.get(), "On", methods))
JS::RootedObject proto(cx);
if (!protoVal.isObjectOrNull())
return; // error
proto = protoVal.toObjectOrNull();
if (!componentManager->m_ScriptInterface.EnumeratePropertyNamesWithPrefix(protoVal, "On", methods))
return; // error
for (std::vector<std::string>::const_iterator it = methods.begin(); it != methods.end(); ++it)
@ -287,9 +294,11 @@ void CComponentManager::Script_RegisterComponentType(ScriptInterface::CxPrivate*
std::map<entity_id_t, IComponent*>::const_iterator eit = comps.begin();
for (; eit != comps.end(); ++eit)
{
jsval instance = eit->second->GetJSInstance();
if (!JSVAL_IS_NULL(instance))
componentManager->m_ScriptInterface.SetPrototype(instance, proto.get());
JS::RootedValue instance(cx, eit->second->GetJSInstance());
if (!instance.isNull())
{
componentManager->m_ScriptInterface.SetPrototype(instance, protoVal);
}
}
}
}
@ -616,7 +625,7 @@ IComponent* CComponentManager::ConstructComponent(CEntityHandle ent, ComponentTy
jsval obj = JSVAL_NULL;
if (ct.type == CT_Script)
{
obj = m_ScriptInterface.CallConstructor(ct.ctor.get(), JSVAL_VOID);
obj = m_ScriptInterface.CallConstructor(ct.ctor.get(), 0, JSVAL_VOID);
if (JSVAL_IS_VOID(obj))
{
LOGERROR(L"Script component constructor failed");

View File

@ -52,8 +52,8 @@ class ComponentTestHelper
EComponentTypeId m_Cid;
public:
ComponentTestHelper() :
m_Context(), m_ComponentManager(m_Context, ScriptInterface::CreateRuntime()), m_Cmp(NULL)
ComponentTestHelper(shared_ptr<ScriptRuntime> runtime) :
m_Context(), m_ComponentManager(m_Context, runtime), m_Cmp(NULL)
{
m_ComponentManager.LoadComponentTypes();
}
@ -135,7 +135,7 @@ public:
CStdSerializer std1(GetScriptInterface(), stdstr1);
m_Cmp->Serialize(std1);
ComponentTestHelper test2;
ComponentTestHelper test2(GetScriptInterface().GetRuntime());
// (We should never need to add any mock objects etc to test2, since deserialization
// mustn't depend on other components already existing)

View File

@ -19,14 +19,12 @@
#define INCLUDED_INTERFACE_SCRIPTED
#include "scriptinterface/ScriptInterface.h"
#include "js/jsapi.h"
#define BEGIN_INTERFACE_WRAPPER(iname) \
JSClass class_ICmp##iname = { \
"ICmp" #iname, JSCLASS_HAS_PRIVATE, \
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, \
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, \
JSCLASS_NO_OPTIONAL_MEMBERS \
JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, \
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub \
}; \
static JSFunctionSpec methods_ICmp##iname[] = {
@ -35,7 +33,8 @@
}; \
void ICmp##iname::InterfaceInit(ScriptInterface& scriptInterface) { \
JSContext* cx = scriptInterface.GetContext(); \
JSObject* global = JS_GetGlobalObject(cx); \
JSAutoRequest rq(cx); \
JSObject* global = JS_GetGlobalForScopeChain(cx); \
JS_InitClass(cx, global, NULL, &class_ICmp##iname, NULL, 0, NULL, methods_ICmp##iname, NULL, NULL); \
} \
JSClass* ICmp##iname::GetJSClass() const { return &class_ICmp##iname; } \
@ -45,49 +44,49 @@
#define DEFINE_INTERFACE_METHOD_0(scriptname, rettype, classname, methodname) \
{ scriptname, \
ScriptInterface::callMethod<rettype, &class_##classname, classname, &classname::methodname>, \
{ ScriptInterface::callMethod<rettype, &class_##classname, classname, &classname::methodname>, NULL }, \
0, \
JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT },
#define DEFINE_INTERFACE_METHOD_1(scriptname, rettype, classname, methodname, arg1) \
{ scriptname, \
ScriptInterface::callMethod<rettype, arg1, &class_##classname, classname, &classname::methodname>, \
{ ScriptInterface::callMethod<rettype, arg1, &class_##classname, classname, &classname::methodname>, NULL}, \
1, \
JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT },
#define DEFINE_INTERFACE_METHOD_2(scriptname, rettype, classname, methodname, arg1, arg2) \
{ scriptname, \
ScriptInterface::callMethod<rettype, arg1, arg2, &class_##classname, classname, &classname::methodname>, \
{ ScriptInterface::callMethod<rettype, arg1, arg2, &class_##classname, classname, &classname::methodname>, NULL }, \
2, \
JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT },
#define DEFINE_INTERFACE_METHOD_3(scriptname, rettype, classname, methodname, arg1, arg2, arg3) \
{ scriptname, \
ScriptInterface::callMethod<rettype, arg1, arg2, arg3, &class_##classname, classname, &classname::methodname>, \
{ ScriptInterface::callMethod<rettype, arg1, arg2, arg3, &class_##classname, classname, &classname::methodname>, NULL }, \
3, \
JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT },
#define DEFINE_INTERFACE_METHOD_4(scriptname, rettype, classname, methodname, arg1, arg2, arg3, arg4) \
{ scriptname, \
ScriptInterface::callMethod<rettype, arg1, arg2, arg3, arg4, &class_##classname, classname, &classname::methodname>, \
{ ScriptInterface::callMethod<rettype, arg1, arg2, arg3, arg4, &class_##classname, classname, &classname::methodname>, NULL }, \
4, \
JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT },
#define DEFINE_INTERFACE_METHOD_5(scriptname, rettype, classname, methodname, arg1, arg2, arg3, arg4, arg5) \
{ scriptname, \
ScriptInterface::callMethod<rettype, arg1, arg2, arg3, arg4, arg5, &class_##classname, classname, &classname::methodname>, \
{ ScriptInterface::callMethod<rettype, arg1, arg2, arg3, arg4, arg5, &class_##classname, classname, &classname::methodname>, NULL }, \
5, \
JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT },
#define DEFINE_INTERFACE_METHOD_6(scriptname, rettype, classname, methodname, arg1, arg2, arg3, arg4, arg5, arg6) \
{ scriptname, \
ScriptInterface::callMethod<rettype, arg1, arg2, arg3, arg4, arg5, arg6, &class_##classname, classname, &classname::methodname>, \
{ ScriptInterface::callMethod<rettype, arg1, arg2, arg3, arg4, arg5, arg6, &class_##classname, classname, &classname::methodname>, NULL }, \
6, \
JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT },
#define DEFINE_INTERFACE_METHOD_7(scriptname, rettype, classname, methodname, arg1, arg2, arg3, arg4, arg5, arg6, arg7) \
{ scriptname, \
ScriptInterface::callMethod<rettype, arg1, arg2, arg3, arg4, arg5, arg6, arg7, &class_##classname, classname, &classname::methodname>, \
{ ScriptInterface::callMethod<rettype, arg1, arg2, arg3, arg4, arg5, arg6, arg7, &class_##classname, classname, &classname::methodname>, NULL }, \
7, \
JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT },

View File

@ -25,8 +25,6 @@
#include "ps/Filesystem.h"
#include "ps/XML/Xeromyces.h"
#include "js/jsapi.h"
#include <sstream>
// Disable "'boost::algorithm::detail::is_classifiedF' : assignment operator could not be generated"
@ -328,6 +326,7 @@ jsval CParamNode::ToJSVal(JSContext* cx, bool cacheValue) const
jsval CParamNode::ConstructJSVal(JSContext* cx) const
{
JSAutoRequest rq(cx);
if (m_Childs.empty())
{
// Empty node - map to undefined
@ -351,8 +350,8 @@ jsval CParamNode::ConstructJSVal(JSContext* cx) const
for (std::map<std::string, CParamNode>::const_iterator it = m_Childs.begin(); it != m_Childs.end(); ++it)
{
jsval childVal = it->second.ConstructJSVal(cx);
if (!JS_SetProperty(cx, obj, it->first.c_str(), &childVal))
JS::RootedValue childVal(cx, it->second.ConstructJSVal(cx));
if (!JS_SetProperty(cx, obj, it->first.c_str(), childVal.address()))
return JSVAL_VOID; // TODO: report error
}
@ -363,8 +362,8 @@ jsval CParamNode::ConstructJSVal(JSContext* cx) const
JSString* str = JS_InternUCStringN(cx, reinterpret_cast<const jschar*>(text.data()), text.length());
if (!str)
return JSVAL_VOID; // TODO: report error
jsval childVal = STRING_TO_JSVAL(str);
if (!JS_SetProperty(cx, obj, "_string", &childVal))
JS::RootedValue childVal(cx, STRING_TO_JSVAL(str));
if (!JS_SetProperty(cx, obj, "_string", childVal.address()))
return JSVAL_VOID; // TODO: report error
}

View File

@ -127,25 +127,26 @@ public:
TS_ASSERT(tempMan != NULL);
tempMan->DisableValidation();
CScriptValRooted val;
JSContext* cx = man.GetScriptInterface().GetContext();
JSAutoRequest rq(cx);
// This is testing some bugs in the template JS object caching
const CParamNode* inherit1 = tempMan->LoadTemplate(ent2, "inherit1", -1);
val = CScriptValRooted(cx, ScriptInterface::ToJSVal(cx, inherit1));
JS::RootedValue val(cx);
ScriptInterface::ToJSVal(cx, val.get(), inherit1);
TS_ASSERT_WSTR_EQUALS(man.GetScriptInterface().ToString(val.get()), L"({Test1A:{'@a':\"a1\", '@b':\"b1\", '@c':\"c1\", d:\"d1\", e:\"e1\", f:\"f1\"}})");
const CParamNode* inherit2 = tempMan->LoadTemplate(ent2, "inherit2", -1);
val = CScriptValRooted(cx, ScriptInterface::ToJSVal(cx, inherit2));
ScriptInterface::ToJSVal(cx, val.get(), inherit2);
TS_ASSERT_WSTR_EQUALS(man.GetScriptInterface().ToString(val.get()), L"({'@parent':\"inherit1\", Test1A:{'@a':\"a2\", '@b':\"b1\", '@c':\"c1\", d:\"d2\", e:\"e1\", f:\"f1\", g:\"g2\"}})");
const CParamNode* actor = tempMan->LoadTemplate(ent2, "actor|example1", -1);
val = CScriptValRooted(cx, ScriptInterface::ToJSVal(cx, &actor->GetChild("VisualActor")));
ScriptInterface::ToJSVal(cx, val.get(), &actor->GetChild("VisualActor"));
TS_ASSERT_WSTR_EQUALS(man.GetScriptInterface().ToString(val.get()), L"({Actor:\"example1\", ActorOnly:(void 0), SilhouetteDisplay:\"false\", SilhouetteOccluder:\"false\", VisibleInAtlasOnly:\"false\"})");
const CParamNode* foundation = tempMan->LoadTemplate(ent2, "foundation|actor|example1", -1);
val = CScriptValRooted(cx, ScriptInterface::ToJSVal(cx, &foundation->GetChild("VisualActor")));
ScriptInterface::ToJSVal(cx, val.get(), &foundation->GetChild("VisualActor"));
TS_ASSERT_WSTR_EQUALS(man.GetScriptInterface().ToString(val.get()), L"({Actor:\"example1\", ActorOnly:(void 0), Foundation:(void 0), SilhouetteDisplay:\"false\", SilhouetteOccluder:\"false\", VisibleInAtlasOnly:\"false\"})");
}

View File

@ -594,7 +594,8 @@ public:
void test_serialization()
{
CSimContext context;
CComponentManager man(context, ScriptInterface::CreateRuntime());
shared_ptr<ScriptRuntime> runtime = ScriptInterface::CreateRuntime();
CComponentManager man(context, runtime);
man.LoadComponentTypes();
entity_id_t ent1 = 1, ent2 = 2, ent3 = FIRST_LOCAL_ENTITY;
@ -664,7 +665,7 @@ public:
);
CSimContext context2;
CComponentManager man2(context2, ScriptInterface::CreateRuntime());
CComponentManager man2(context2, runtime);
man2.LoadComponentTypes();
TS_ASSERT(man2.QueryInterface(ent1, IID_Test1) == NULL);
@ -683,7 +684,9 @@ public:
void test_script_serialization()
{
CSimContext context;
CComponentManager man(context, ScriptInterface::CreateRuntime());
shared_ptr<ScriptRuntime> runtime = ScriptInterface::CreateRuntime();
CComponentManager man(context, runtime);
ScriptTestSetup(man.m_ScriptInterface);
man.LoadComponentTypes();
TS_ASSERT(man.LoadScript(L"simulation/components/test-serialize.js"));
@ -700,7 +703,12 @@ public:
man.AddComponent(hnd1, man.LookupCID("TestScript1_values"), testParam);
man.AddComponent(hnd2, man.LookupCID("TestScript1_entity"), testParam);
// TODO: Since the upgrade to SpiderMonkey v24 this test won't be able to correctly represent
// non-tree structures because sharp variables were removed (bug 566700).
// This also affects the debug serializer and it could make sense to implement correct serialization again.
man.AddComponent(hnd3, man.LookupCID("TestScript1_nontree"), testParam);
man.AddComponent(hnd4, man.LookupCID("TestScript1_custom"), testParam);
TS_ASSERT_EQUALS(static_cast<ICmpTest1*> (man.QueryInterface(ent1, IID_Test1))->GetX(), 1234);
@ -713,48 +721,48 @@ public:
std::stringstream debugStream;
TS_ASSERT(man.DumpDebugState(debugStream, true));
TS_ASSERT_STR_EQUALS(debugStream.str(),
"rng: \"78606\"\n"
"entities:\n"
"- id: 1\n"
" TestScript1_values:\n"
" object: {\n"
" \"x\": 1234,\n"
" \"str\": \"this is a string\",\n"
" \"things\": {\n"
" \"a\": 1,\n"
" \"b\": \"2\",\n"
" \"c\": [\n"
" 3,\n"
" \"4\",\n"
" [\n"
" 5,\n"
" []\n"
" ]\n"
" ]\n"
" }\n"
"}\n"
"\n"
"- id: 2\n"
" TestScript1_entity:\n"
" object: {}\n"
"\n"
"- id: 3\n"
" TestScript1_nontree:\n"
" object: ({x:#2=[#1=[2], #1#, #2#, {y:#1#}]})\n"
"\n"
"- id: 4\n"
" TestScript1_custom:\n"
" object: {\n"
" \"c\": 1\n"
"}\n"
"\n"
"rng: \"78606\"\n\
entities:\n\
- id: 1\n\
TestScript1_values:\n\
object: {\n\
\"x\": 1234,\n\
\"str\": \"this is a string\",\n\
\"things\": {\n\
\"a\": 1,\n\
\"b\": \"2\",\n\
\"c\": [\n\
3,\n\
\"4\",\n\
[\n\
5,\n\
[]\n\
]\n\
]\n\
}\n\
}\n\
\n\
- id: 2\n\
TestScript1_entity:\n\
object: {}\n\
\n\
- id: 3\n\
TestScript1_nontree:\n\
object: ({x:[[2], [2], [], {y:[2]}]})\n\
\n\
- id: 4\n\
TestScript1_custom:\n\
object: {\n\
\"c\": 1\n\
}\n\
\n"
);
std::stringstream stateStream;
TS_ASSERT(man.SerializeState(stateStream));
CSimContext context2;
CComponentManager man2(context2, ScriptInterface::CreateRuntime());
CComponentManager man2(context2, runtime);
man2.LoadComponentTypes();
TS_ASSERT(man2.LoadScript(L"simulation/components/test-serialize.js"));
@ -789,7 +797,9 @@ public:
void test_script_serialization_template()
{
CSimContext context;
CComponentManager man(context, ScriptInterface::CreateRuntime());
shared_ptr<ScriptRuntime> runtime = ScriptInterface::CreateRuntime();
CComponentManager man(context, runtime);
man.LoadComponentTypes();
TS_ASSERT(man.LoadScript(L"simulation/components/test-serialize.js"));
man.InitSystemEntity();
@ -813,7 +823,7 @@ public:
TS_ASSERT(man.SerializeState(stateStream));
CSimContext context2;
CComponentManager man2(context2, ScriptInterface::CreateRuntime());
CComponentManager man2(context2, runtime);
man2.LoadComponentTypes();
TS_ASSERT(man2.LoadScript(L"simulation/components/test-serialize.js"));

View File

@ -574,9 +574,6 @@ public:
TestLogger logger;
TS_ASSERT(script.Eval("({x:1, y:<x/>})", obj));
TS_ASSERT_THROWS(serialize.ScriptVal("script", obj), PSERROR_Serialize_InvalidScriptValue);
TS_ASSERT(script.Eval("([1, 2, function () { }])", obj));
TS_ASSERT_THROWS(serialize.ScriptVal("script", obj), PSERROR_Serialize_InvalidScriptValue);
}