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:
parent
6f6b841af3
commit
e9e05f4efc
@ -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
|
||||
|
@ -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)" }
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
16
libraries/osx/patches/sm-mavericks-clang-fix.diff
Normal file
16
libraries/osx/patches/sm-mavericks-clang-fix.diff
Normal 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'
|
@ -30,7 +30,7 @@ class CCinemaManager;
|
||||
class CVector3D;
|
||||
struct SViewPort;
|
||||
|
||||
struct JSObject;
|
||||
class JSObject;
|
||||
|
||||
class CGameViewImpl;
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -30,7 +30,7 @@
|
||||
#include <set>
|
||||
|
||||
class CGUI;
|
||||
struct JSObject;
|
||||
class JSObject;
|
||||
class IGUIObject;
|
||||
struct CColor;
|
||||
struct SGUIIcon;
|
||||
|
@ -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());;
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 );
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,6 @@
|
||||
|
||||
#include "scriptinterface/ScriptTypes.h"
|
||||
|
||||
#include "js/jsapi.h"
|
||||
|
||||
/**
|
||||
* Helper for rooting large groups of script values.
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
@ -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);
|
||||
}
|
||||
|
@ -21,8 +21,6 @@
|
||||
|
||||
#include "scriptinterface/ScriptInterface.h"
|
||||
|
||||
#include "js/jsapi.h"
|
||||
|
||||
CScriptStatsTable* g_ScriptStatsTable;
|
||||
|
||||
enum
|
||||
|
@ -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;
|
||||
|
@ -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];
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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));
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -34,7 +34,7 @@ public:
|
||||
|
||||
void test_basic()
|
||||
{
|
||||
ComponentTestHelper test;
|
||||
ComponentTestHelper test(ScriptInterface::CreateRuntime());
|
||||
|
||||
std::vector<SimulationCommand> empty;
|
||||
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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);
|
||||
|
@ -77,7 +77,7 @@ public:
|
||||
|
||||
void test_basic()
|
||||
{
|
||||
ComponentTestHelper test;
|
||||
ComponentTestHelper test(ScriptInterface::CreateRuntime());
|
||||
|
||||
ICmpRangeManager* cmp = test.Add<ICmpRangeManager>(CID_RangeManager, "");
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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");
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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 },
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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\"})");
|
||||
}
|
||||
|
||||
|
@ -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"));
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user