diff --git a/build/premake/extern_libs.lua b/build/premake/extern_libs.lua index 3cfa5abf09..fb0885852c 100644 --- a/build/premake/extern_libs.lua +++ b/build/premake/extern_libs.lua @@ -190,13 +190,13 @@ extern_lib_defs = { else include_dir = "include-unix" end - tinsert(package.config["Debug" ].includepaths, libraries_dir.."spidermonkey-tip/"..include_dir.."/debug") - tinsert(package.config["Testing"].includepaths, libraries_dir.."spidermonkey-tip/"..include_dir.."/debug") - tinsert(package.config["Release"].includepaths, libraries_dir.."spidermonkey-tip/"..include_dir.."/release") - tinsert(package.libpaths, libraries_dir.."spidermonkey-tip/lib") - tinsert(package.config["Debug" ].links, "mozjs-ps-debug") - tinsert(package.config["Testing"].links, "mozjs-ps-release") - tinsert(package.config["Release"].links, "mozjs-ps-release") + tinsert(package.config["Debug" ].includepaths, libraries_dir.."spidermonkey/"..include_dir) + tinsert(package.config["Testing"].includepaths, libraries_dir.."spidermonkey/"..include_dir) + tinsert(package.config["Release"].includepaths, libraries_dir.."spidermonkey/"..include_dir) + tinsert(package.libpaths, libraries_dir.."spidermonkey/lib") + tinsert(package.config["Debug" ].links, "mozjs185-ps-debug") + tinsert(package.config["Testing"].links, "mozjs185-ps-release") + tinsert(package.config["Release"].links, "mozjs185-ps-release") end, }, valgrind = { diff --git a/build/premake/extern_libs4.lua b/build/premake/extern_libs4.lua index bc2b8bd97f..0e8b6320bf 100644 --- a/build/premake/extern_libs4.lua +++ b/build/premake/extern_libs4.lua @@ -485,22 +485,22 @@ extern_lib_defs = { include_dir = "include-unix" end configuration "Debug" - includedirs { libraries_dir.."spidermonkey-tip/"..include_dir.."/debug" } + includedirs { libraries_dir.."spidermonkey/"..include_dir } configuration "Testing" - includedirs { libraries_dir.."spidermonkey-tip/"..include_dir.."/debug" } + includedirs { libraries_dir.."spidermonkey/"..include_dir } configuration "Release" - includedirs { libraries_dir.."spidermonkey-tip/"..include_dir.."/release" } + includedirs { libraries_dir.."spidermonkey/"..include_dir } configuration { } end, link_settings = function() configuration "Debug" - links { "mozjs-ps-debug" } + links { "mozjs185-ps-debug" } configuration "Testing" - links { "mozjs-ps-debug" } + links { "mozjs185-ps-debug" } configuration "Release" - links { "mozjs-ps-release" } + links { "mozjs185-ps-release" } configuration { } - add_default_lib_paths("spidermonkey-tip") + add_default_lib_paths("spidermonkey") end, }, valgrind = { diff --git a/build/workspaces/update-workspaces-new.sh b/build/workspaces/update-workspaces-new.sh index 0434b7e363..2f5b21f814 100644 --- a/build/workspaces/update-workspaces-new.sh +++ b/build/workspaces/update-workspaces-new.sh @@ -50,7 +50,7 @@ echo # Build/update bundled external libraries (cd ../../libraries/fcollada/src && make ${JOBS}) || die "FCollada build failed" echo -(cd ../../libraries/spidermonkey-tip && JOBS=${JOBS} ./build.sh) || die "SpiderMonkey build failed" +(cd ../../libraries/spidermonkey && JOBS=${JOBS} ./build.sh) || die "SpiderMonkey build failed" echo if [ "$with_system_nvtt" = "false" ]; then (cd ../../libraries/nvtt && JOBS=${JOBS} ./build.sh) || die "NVTT build failed" diff --git a/build/workspaces/update-workspaces.sh b/build/workspaces/update-workspaces.sh index 6e6a54382f..7f8b96b4e1 100755 --- a/build/workspaces/update-workspaces.sh +++ b/build/workspaces/update-workspaces.sh @@ -49,7 +49,7 @@ echo # Build/update bundled external libraries (cd ../../libraries/fcollada/src && make ${JOBS}) || die "FCollada build failed" echo -(cd ../../libraries/spidermonkey-tip && JOBS=${JOBS} ./build.sh) || die "SpiderMonkey build failed" +(cd ../../libraries/spidermonkey && JOBS=${JOBS} ./build.sh) || die "SpiderMonkey build failed" echo if [ "$with_system_nvtt" = "false" ]; then (cd ../../libraries/nvtt && JOBS=${JOBS} ./build.sh) || die "NVTT build failed" diff --git a/source/gui/CGUI.cpp b/source/gui/CGUI.cpp index 13c0ac44d6..e4f9f10421 100644 --- a/source/gui/CGUI.cpp +++ b/source/gui/CGUI.cpp @@ -325,7 +325,7 @@ static JSBool GetGlobalProperty(JSContext* cx, JSObject* UNUSED(obj), jsid id, j return JS_GetPropertyById(cx, g_ScriptingHost.GetGlobalObject(), id, vp); } -static JSBool SetGlobalProperty(JSContext* cx, JSObject* UNUSED(obj), jsid id, jsval* vp) +static JSBool SetGlobalProperty(JSContext* cx, JSObject* UNUSED(obj), jsid id, JSBool UNUSED(strict), jsval* vp) { return JS_SetPropertyById(cx, g_ScriptingHost.GetGlobalObject(), id, vp); } @@ -376,7 +376,7 @@ static JSBool ResolveGlobalProperty(JSContext* cx, JSObject* obj, jsid id, uintN static JSClass page_global_class = { "page_global", JSCLASS_GLOBAL_FLAGS | JSCLASS_NEW_RESOLVE, - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, (JSResolveOp)ResolveGlobalProperty, JS_ConvertStub, JS_FinalizeStub, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL diff --git a/source/gui/IGUIObject.h b/source/gui/IGUIObject.h index 22b5413853..acb4dc1a3a 100644 --- a/source/gui/IGUIObject.h +++ b/source/gui/IGUIObject.h @@ -146,7 +146,7 @@ class IGUIObject // 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, jsval* vp); + friend JSBool JSI_IGUIObject::setProperty(JSContext* cx, JSObject* obj, jsid id, JSBool strict, jsval* vp); public: IGUIObject(); diff --git a/source/gui/scripting/JSInterface_GUITypes.cpp b/source/gui/scripting/JSInterface_GUITypes.cpp index f24b8e5106..9bf6665972 100644 --- a/source/gui/scripting/JSInterface_GUITypes.cpp +++ b/source/gui/scripting/JSInterface_GUITypes.cpp @@ -24,7 +24,7 @@ JSClass JSI_GUISize::JSI_class = { "GUISize", 0, JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, JS_PropertyStub, + JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, NULL, NULL, NULL, JSI_GUISize::construct @@ -137,7 +137,7 @@ JSBool JSI_GUISize::toString(JSContext* cx, uintN argc, jsval* vp) JSClass JSI_GUIColor::JSI_class = { "GUIColor", 0, JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, JS_PropertyStub, + JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, NULL, NULL, NULL, JSI_GUIColor::construct @@ -214,7 +214,7 @@ JSBool JSI_GUIColor::toString(JSContext* cx, uintN argc, jsval* vp) JSClass JSI_GUIMouse::JSI_class = { "GUIMouse", 0, JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, JS_PropertyStub, + JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, NULL, NULL, NULL, JSI_GUIMouse::construct diff --git a/source/gui/scripting/JSInterface_IGUIObject.cpp b/source/gui/scripting/JSInterface_IGUIObject.cpp index f78d616025..aeb7cf2cfd 100644 --- a/source/gui/scripting/JSInterface_IGUIObject.cpp +++ b/source/gui/scripting/JSInterface_IGUIObject.cpp @@ -290,7 +290,7 @@ JSBool JSI_IGUIObject::getProperty(JSContext* cx, JSObject* obj, jsid id, jsval* } } -JSBool JSI_IGUIObject::setProperty(JSContext* cx, JSObject* obj, jsid id, jsval* vp) +JSBool JSI_IGUIObject::setProperty(JSContext* cx, JSObject* obj, jsid id, JSBool UNUSED(strict), jsval* vp) { IGUIObject* e = (IGUIObject*)JS_GetInstancePrivate(cx, obj, &JSI_IGUIObject::JSI_class, NULL); if (!e) diff --git a/source/gui/scripting/JSInterface_IGUIObject.h b/source/gui/scripting/JSInterface_IGUIObject.h index d971bb158b..3f2bab844f 100644 --- a/source/gui/scripting/JSInterface_IGUIObject.h +++ b/source/gui/scripting/JSInterface_IGUIObject.h @@ -26,7 +26,7 @@ namespace JSI_IGUIObject 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, 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); diff --git a/source/maths/scripting/JSInterface_Vector3D.cpp b/source/maths/scripting/JSInterface_Vector3D.cpp index 2d17e838e9..a07a0e1d3e 100644 --- a/source/maths/scripting/JSInterface_Vector3D.cpp +++ b/source/maths/scripting/JSInterface_Vector3D.cpp @@ -125,7 +125,7 @@ JSBool JSI_Vector3D::getProperty(JSContext* cx, JSObject* obj, jsid id, jsval* v return JS_FALSE; } -JSBool JSI_Vector3D::setProperty(JSContext* cx, JSObject* obj, jsid id, jsval* vp) +JSBool JSI_Vector3D::setProperty(JSContext* cx, JSObject* obj, jsid id, JSBool UNUSED(strict), jsval* vp) { if (!JSID_IS_INT(id)) return JS_TRUE; diff --git a/source/maths/scripting/JSInterface_Vector3D.h b/source/maths/scripting/JSInterface_Vector3D.h index ab08ec9adf..32d39ffae9 100644 --- a/source/maths/scripting/JSInterface_Vector3D.h +++ b/source/maths/scripting/JSInterface_Vector3D.h @@ -57,7 +57,7 @@ namespace JSI_Vector3D extern JSFunctionSpec JSI_methods[]; JSBool getProperty(JSContext* cx, JSObject* obj, jsid id, jsval* vp); - JSBool setProperty(JSContext* cx, JSObject* obj, jsid id, jsval* vp); + JSBool setProperty(JSContext* cx, JSObject* obj, jsid id, JSBool strict, jsval* vp); void finalize(JSContext* cx, JSObject* obj); JSBool construct(JSContext* cx, uintN argc, jsval* vp); void init(); diff --git a/source/ps/ConfigDB.cpp b/source/ps/ConfigDB.cpp index c268f804de..6f4e229b99 100644 --- a/source/ps/ConfigDB.cpp +++ b/source/ps/ConfigDB.cpp @@ -60,7 +60,7 @@ namespace ConfigNamespace_JS return JS_TRUE; } - JSBool SetProperty(JSContext* cx, JSObject* obj, jsid id, jsval* vp) + JSBool SetProperty(JSContext* cx, JSObject* obj, jsid id, JSBool UNUSED(strict), jsval* vp) { EConfigNamespace cfgNs = GET_NS_PRIVATE(cx, obj); if (cfgNs < 0 || cfgNs >= CFG_LAST) @@ -169,7 +169,7 @@ namespace ConfigDB_JS JSClass Class = { "ConfigDB", 0, JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, JS_PropertyStub, + JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub }; diff --git a/source/ps/scripting/JSInterface_Console.cpp b/source/ps/scripting/JSInterface_Console.cpp index 79b3005ece..1ceda66e2e 100644 --- a/source/ps/scripting/JSInterface_Console.cpp +++ b/source/ps/scripting/JSInterface_Console.cpp @@ -62,7 +62,7 @@ JSBool JSI_Console::getProperty(JSContext* UNUSED(cx), JSObject* UNUSED(obj), js } } -JSBool JSI_Console::setProperty(JSContext* UNUSED(cx), JSObject* UNUSED(obj), jsid id, jsval* vp) +JSBool JSI_Console::setProperty(JSContext* UNUSED(cx), JSObject* UNUSED(obj), jsid id, JSBool UNUSED(strict), jsval* vp) { if (!JSID_IS_INT(id)) return JS_TRUE; diff --git a/source/ps/scripting/JSInterface_Console.h b/source/ps/scripting/JSInterface_Console.h index 2512bcb63e..8087af6a5e 100644 --- a/source/ps/scripting/JSInterface_Console.h +++ b/source/ps/scripting/JSInterface_Console.h @@ -35,7 +35,7 @@ namespace JSI_Console extern JSFunctionSpec JSI_methods[]; JSBool getProperty(JSContext* cx, JSObject* obj, jsid id, jsval* vp); - JSBool setProperty(JSContext* cx, JSObject* obj, jsid id, jsval* vp); + JSBool setProperty(JSContext* cx, JSObject* obj, jsid id, JSBool strict, jsval* vp); JSBool getConsole(JSContext* context, JSObject* obj, jsid id, jsval* vp); diff --git a/source/scripting/ScriptableObject.h b/source/scripting/ScriptableObject.h index 9b97e1bb48..c7f3f150ad 100644 --- a/source/scripting/ScriptableObject.h +++ b/source/scripting/ScriptableObject.h @@ -407,7 +407,7 @@ private: return( JS_TRUE ); } - static JSBool JSSetProperty( JSContext* cx, JSObject* obj, jsid id, jsval* vp ) + static JSBool JSSetProperty( JSContext* cx, JSObject* obj, jsid id, JSBool UNUSED(strict), jsval* vp ) { T* Instance = ToNative( cx, obj ); if( !Instance ) diff --git a/source/scripting/ScriptingHost.cpp b/source/scripting/ScriptingHost.cpp index c9841f4368..13462c46fc 100644 --- a/source/scripting/ScriptingHost.cpp +++ b/source/scripting/ScriptingHost.cpp @@ -208,7 +208,10 @@ CStrW ScriptingHost::ValueToUCString( const jsval value ) if (string == NULL) throw PSERROR_Scripting_ConversionFailed(); - jschar *strptr=JS_GetStringChars(string); - size_t length=JS_GetStringLength(string); + size_t length; + const jschar *strptr = JS_GetStringCharsAndLength(m_Context, string, &length); + if (!strptr) + throw PSERROR_Scripting_ConversionFailed(); + return std::wstring(strptr, strptr+length); } diff --git a/source/scriptinterface/NativeWrapperDefns.h b/source/scriptinterface/NativeWrapperDefns.h index e247c7d616..f54cb01bca 100644 --- a/source/scriptinterface/NativeWrapperDefns.h +++ b/source/scriptinterface/NativeWrapperDefns.h @@ -72,11 +72,18 @@ struct ScriptInterface_NativeMethodWrapper { }; // Fast natives don't trigger the hook we use for profiling, so explicitly -// notify the profiler when these functions are being called +// notify the profiler when these functions are being called. +// ScriptInterface_impl::Register stores the name in a reserved slot. +// (TODO: this doesn't work for functions registered via InterfaceScripted.h. +// Maybe we should do some interned JS_GetFunctionId thing.) #if ENABLE_SCRIPT_PROFILING #define SCRIPT_PROFILE \ ENSURE(JSVAL_IS_OBJECT(JS_CALLEE(cx, vp)) && JS_ObjectIsFunction(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)))); \ - const char* name = JS_GetFunctionName(JS_ValueToFunction(cx, JS_CALLEE(cx, vp))); /* native function so ValueToFunction is safe; this makes unsafe lifetime assumptions */ \ + const char* name = "(unknown)"; \ + jsval nameval; \ + if (JS_GetReservedSlot(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)), 0, &nameval) \ + && !JSVAL_IS_VOID(nameval)) \ + name = static_cast(JSVAL_TO_PRIVATE(nameval)); \ CProfileSampleScript profile(name); #else #define SCRIPT_PROFILE diff --git a/source/scriptinterface/ScriptConversions.cpp b/source/scriptinterface/ScriptConversions.cpp index 9b1d2c9e0c..e62895664c 100644 --- a/source/scriptinterface/ScriptConversions.cpp +++ b/source/scriptinterface/ScriptConversions.cpp @@ -23,12 +23,7 @@ #include "ps/utf16string.h" #include "ps/CLogger.h" #include "ps/CStr.h" - -#include "js/jsapi.h" - -#define signbit std::signbit -#include "js/jstypedarray.h" -#undef signbit +#include "scriptinterface/ScriptExtraHeaders.h" // for typed arrays #define FAIL(msg) STMT(JS_ReportError(cx, msg); return false) @@ -114,8 +109,11 @@ template<> bool ScriptInterface::FromJSVal(JSContext* cx, jsval v, JSString* ret = JS_ValueToString(cx, v); if (!ret) FAIL("Argument must be convertible to a string"); - jschar* ch = JS_GetStringChars(ret); - out = std::wstring(ch, ch + JS_GetStringLength(ret)); + size_t length; + const jschar* ch = JS_GetStringCharsAndLength(cx, ret, &length); + if (!ch) + FAIL("JS_GetStringsCharsAndLength failed"); // out of memory + out = std::wstring(ch, ch + length); return true; } diff --git a/source/scriptinterface/ScriptExtraHeaders.h b/source/scriptinterface/ScriptExtraHeaders.h new file mode 100644 index 0000000000..5562bbc470 --- /dev/null +++ b/source/scriptinterface/ScriptExtraHeaders.h @@ -0,0 +1,53 @@ +/* Copyright (C) 2011 Wildfire Games. + * This file is part of 0 A.D. + * + * 0 A.D. is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 0 A.D. is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 0 A.D. If not, see . + */ + +#ifndef INCLUDED_SCRIPTEXTRAHEADERS +#define INCLUDED_SCRIPTEXTRAHEADERS + +// Includes occasionally-used SpiderMonkey headers for typed arrays and debug API, +// with appropriate tweaks to fix warnings and build errors. (Most code should +// just include ScriptTypes.h directly to get the standard jsapi.h.) + +#include "scriptinterface/ScriptTypes.h" + +// Ignore some harmless warnings +#if GCC_VERSION >= 402 // (older GCCs don't support this pragma) +# pragma GCC diagnostic ignored "-Wunused-parameter" +# pragma GCC diagnostic ignored "-Wredundant-decls" +#endif +#if MSC_VERSION +# pragma warning(push) +# pragma warning(disable:4800) // "forcing value to bool 'true' or 'false' (performance warning)" +#endif + +// Redefine signbit to fix build error in GCC +#define signbit std::signbit + +#include "js/jstypedarray.h" +#include "js/jsdbgapi.h" + +#undef signbit + +#if MSC_VERSION +# pragma warning(pop) +#endif +#if GCC_VERSION >= 402 +# pragma GCC diagnostic ignored "-Wunused-parameter" +# pragma GCC diagnostic warning "-Wredundant-decls" +#endif + +#endif // INCLUDED_SCRIPTEXTRAHEADERS diff --git a/source/scriptinterface/ScriptInterface.cpp b/source/scriptinterface/ScriptInterface.cpp index b56114c4f8..c175890393 100644 --- a/source/scriptinterface/ScriptInterface.cpp +++ b/source/scriptinterface/ScriptInterface.cpp @@ -45,11 +45,7 @@ #define STACK_CHUNK_SIZE 8192 -#if ENABLE_SCRIPT_PROFILING -# define signbit std::signbit -# include "js/jsdbgapi.h" -# undef signbit -#endif +#include "scriptinterface/ScriptExtraHeaders.h" //////////////////////////////////////////////////////////////// @@ -57,7 +53,7 @@ class ScriptRuntime { public: ScriptRuntime(int runtimeSize) : - m_rooter(NULL) + m_rooter(NULL), m_compartmentGlobal(NULL) { m_rt = JS_NewRuntime(runtimeSize); ENSURE(m_rt); // TODO: error handling @@ -85,6 +81,8 @@ public: JSRuntime* m_rt; AutoGCRooter* m_rooter; + JSObject* m_compartmentGlobal; + private: #if ENABLE_SCRIPT_PROFILING @@ -227,6 +225,7 @@ struct ScriptInterface_impl JSContext* m_cx; JSObject* m_glob; // global scope object JSObject* m_nativeScope; // native function scope object + JSCrossCompartmentCall* m_call; }; namespace @@ -234,7 +233,7 @@ namespace JSClass global_class = { "global", JSCLASS_GLOBAL_FLAGS, - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL @@ -265,6 +264,10 @@ void ErrorReporter(JSContext* cx, const char* message, JSErrorReport* report) { // TODO: this violates the docs ("The error reporter callback must not reenter the JSAPI.") + // Hide the exception from EvaluateScript + JSExceptionState* excnState = JS_SaveExceptionState(cx); + JS_ClearPendingException(cx); + jsval rval; const char dumpStack[] = "this.stack.trimRight().replace(/^/mg, ' ')"; // indent each line if (JS_EvaluateScript(cx, JSVAL_TO_OBJECT(excn), dumpStack, ARRAY_SIZE(dumpStack)-1, "(eval)", 1, &rval)) @@ -272,6 +275,13 @@ void ErrorReporter(JSContext* cx, const char* message, JSErrorReport* report) std::string stackTrace; if (ScriptInterface::FromJSVal(cx, rval, stackTrace)) msg << "\n" << stackTrace; + + JS_RestoreExceptionState(cx, excnState); + } + else + { + // Error got replaced by new exception from EvaluateScript + JS_DropExceptionState(cx, excnState); } } @@ -357,7 +367,15 @@ JSBool deepcopy(JSContext* cx, uintN argc, jsval* vp) } jsval ret; - if (!JS_StructuredClone(cx, JS_ARGV(cx, vp)[0], &ret)) + + // We'd usually do: + // if (!JS_StructuredClone(cx, JS_ARGV(cx, vp)[0], &ret, NULL, NULL)) + // return JS_FALSE; + // but that function is broken in the 1.8.5 release + // (https://bugzilla.mozilla.org/show_bug.cgi?id=651510) + // so do an equivalent operation with a different API: + JSAutoStructuredCloneBuffer buf; + if (!buf.write(cx, JS_ARGV(cx, vp)[0]) || !buf.read(&ret, cx)) return JS_FALSE; JS_SET_RVAL(cx, vp, ret); @@ -463,7 +481,19 @@ ScriptInterface_impl::ScriptInterface_impl(const char* nativeScopeName, const sh // Threadsafe SpiderMonkey requires that we have a request before doing anything much JS_BeginRequest(m_cx); - m_glob = JS_NewGlobalObject(m_cx, &global_class); + // We only want a single compartment per runtime + if (m_runtime->m_compartmentGlobal) + { + m_call = JS_EnterCrossCompartmentCall(m_cx, m_runtime->m_compartmentGlobal); + m_glob = JS_NewGlobalObject(m_cx, &global_class); + } + else + { + m_call = NULL; + m_glob = JS_NewCompartmentAndGlobalObject(m_cx, &global_class, NULL); + m_runtime->m_compartmentGlobal = m_glob; + } + ok = JS_InitStandardClasses(m_cx, m_glob); ENSURE(ok); @@ -485,13 +515,33 @@ ScriptInterface_impl::ScriptInterface_impl(const char* nativeScopeName, const sh ScriptInterface_impl::~ScriptInterface_impl() { + if (m_call) + JS_LeaveCrossCompartmentCall(m_call); JS_EndRequest(m_cx); JS_DestroyContext(m_cx); } void ScriptInterface_impl::Register(const char* name, JSNative fptr, uintN nargs) { - JS_DefineFunction(m_cx, m_nativeScope, name, fptr, nargs, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT); + JSFunction* func = JS_DefineFunction(m_cx, m_nativeScope, name, fptr, nargs, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT); + + if (!func) + return; + +#if ENABLE_SCRIPT_PROFILING + // Store the function name in a slot, so we can pass it to the profiler. + + // Use a flyweight std::string because we can't assume the caller has + // a correctly-aligned string and that its lifetime is long enough + typedef boost::flyweight< + std::string, + boost::flyweights::no_tracking + // can't use no_locking; Register might be called in threads + > LockedStringFlyweight; + + LockedStringFlyweight fw(name); + JS_SetReservedSlot(m_cx, JS_GetFunctionObject(func), 0, PRIVATE_TO_JSVAL((void*)fw.get().c_str())); +#endif } ScriptInterface::ScriptInterface(const char* nativeScopeName, const char* debugName, const shared_ptr& runtime) : @@ -762,9 +812,9 @@ bool ScriptInterface::EnumeratePropertyNamesWithPrefix(jsval obj, const char* pr continue; // ignore integer properties JSString* name = JSVAL_TO_STRING(val); - size_t len = JS_GetStringLength(name); - jschar* chars = JS_GetStringChars(name); - if (len >= prefix16.size() && memcmp(chars, prefix16.c_str(), prefix16.size()*2) == 0) + size_t len; + const jschar* chars = JS_GetStringCharsAndLength(m->m_cx, name, &len); + if (chars && len >= prefix16.size() && memcmp(chars, prefix16.c_str(), prefix16.size()*2) == 0) out.push_back(std::string(chars, chars+len)); // handles Unicode poorly } @@ -1102,7 +1152,10 @@ private: if (JSVAL_IS_STRING(val)) { - JSString* str = JS_NewUCStringCopyN(cxTo, JS_GetStringChars(JSVAL_TO_STRING(val)), JS_GetStringLength(JSVAL_TO_STRING(val))); + size_t len; + const jschar* chars = JS_GetStringCharsAndLength(cxFrom, JSVAL_TO_STRING(val), &len); + CLONE_REQUIRE(chars, L"JS_GetStringCharsAndLength"); + JSString* str = JS_NewUCStringCopyN(cxTo, chars, len); CLONE_REQUIRE(str, L"JS_NewUCStringCopyN"); jsval rval = STRING_TO_JSVAL(str); m_Mapping[JSVAL_TO_GCTHING(val)] = rval; @@ -1153,7 +1206,10 @@ private: // string jsids are runtime-specific, so we need to copy the string content JSString* idstr = JS_ValueToString(cxFrom, idval); CLONE_REQUIRE(idstr, L"JS_ValueToString (id)"); - CLONE_REQUIRE(JS_SetUCProperty(cxTo, newObj, JS_GetStringChars(idstr), JS_GetStringLength(idstr), &newPropval), L"JS_SetUCProperty"); + size_t len; + const jschar* chars = JS_GetStringCharsAndLength(cxFrom, idstr, &len); + CLONE_REQUIRE(idstr, L"JS_GetStringCharsAndLength (id)"); + CLONE_REQUIRE(JS_SetUCProperty(cxTo, newObj, chars, len, &newPropval), L"JS_SetUCProperty"); } else { @@ -1195,7 +1251,7 @@ shared_ptr ScriptInterface::WriteStructuredClo { uint64* data = NULL; size_t nbytes = 0; - if (!JS_WriteStructuredClone(m->m_cx, v, &data, &nbytes)) + if (!JS_WriteStructuredClone(m->m_cx, v, &data, &nbytes, NULL, NULL)) return shared_ptr(); // TODO: should we have better error handling? // Currently we'll probably continue and then crash in ReadStructuredClone @@ -1210,6 +1266,6 @@ shared_ptr ScriptInterface::WriteStructuredClo jsval ScriptInterface::ReadStructuredClone(const shared_ptr& ptr) { jsval ret = JSVAL_VOID; - JS_ReadStructuredClone(m->m_cx, ptr->m_Data, ptr->m_Size, &ret); + JS_ReadStructuredClone(m->m_cx, ptr->m_Data, ptr->m_Size, JS_STRUCTURED_CLONE_VERSION, &ret, NULL, NULL); return ret; } diff --git a/source/scriptinterface/ScriptTypes.h b/source/scriptinterface/ScriptTypes.h index 0f84ee4969..5134f5f712 100644 --- a/source/scriptinterface/ScriptTypes.h +++ b/source/scriptinterface/ScriptTypes.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010 Wildfire Games. +/* Copyright (C) 2011 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -47,8 +47,27 @@ # endif #endif +// Ignore some harmless warnings triggered by jsapi.h +#if GCC_VERSION >= 402 // (older GCCs don't support this pragma) +# pragma GCC diagnostic ignored "-Wunused-parameter" +# pragma GCC diagnostic ignored "-Wredundant-decls" +#endif +#if MSC_VERSION +# pragma warning(push) +# pragma warning(disable:4480) // "nonstandard extension used: specifying underlying type for enum" +# pragma warning(disable:4100) // "unreferenced formal parameter" +#endif + #include "js/jsapi.h" +#if MSC_VERSION +# pragma warning(pop) +#endif +#if GCC_VERSION >= 402 +# pragma GCC diagnostic warning "-Wunused-parameter" +# pragma GCC diagnostic warning "-Wredundant-decls" +#endif + #if JS_VERSION != 185 #error Your compiler is trying to use an incorrect version of the SpiderMonkey library. #error The only version that works is the one in the libraries/spidermonkey-tip/ directory, diff --git a/source/simulation2/components/CCmpAIManager.cpp b/source/simulation2/components/CCmpAIManager.cpp index 5057cd559c..57bfce518d 100644 --- a/source/simulation2/components/CCmpAIManager.cpp +++ b/source/simulation2/components/CCmpAIManager.cpp @@ -90,6 +90,7 @@ private: { // Clean up rooted objects before destroying their script context m_Obj = CScriptValRooted(); + m_Commands.clear(); } static void IncludeModule(void* cbdata, std::wstring name) @@ -270,6 +271,8 @@ public: m_EntityTemplates = CScriptValRooted(); m_PlayerMetadata.clear(); m_Players.clear(); + m_GameState.reset(); + m_GameStateMapVal = CScriptValRooted(); } bool AddPlayer(const std::wstring& aiName, player_id_t player, bool callConstructor) diff --git a/source/simulation2/scripting/EngineScriptConversions.cpp b/source/simulation2/scripting/EngineScriptConversions.cpp index b19b169be1..3e43414b6a 100644 --- a/source/simulation2/scripting/EngineScriptConversions.cpp +++ b/source/simulation2/scripting/EngineScriptConversions.cpp @@ -18,6 +18,7 @@ #include "precompiled.h" #include "scriptinterface/ScriptInterface.h" +#include "scriptinterface/ScriptExtraHeaders.h" // for typed arrays #include "maths/Fixed.h" #include "maths/FixedVector2D.h" @@ -29,12 +30,6 @@ #include "simulation2/system/IComponent.h" #include "simulation2/system/ParamNode.h" -#include "js/jsapi.h" - -#define signbit std::signbit -#include "js/jstypedarray.h" -#undef signbit - #define FAIL(msg) STMT(JS_ReportError(cx, msg); return false) template<> jsval ScriptInterface::ToJSVal(JSContext* cx, IComponent* const& val) diff --git a/source/simulation2/serialization/BinarySerializer.cpp b/source/simulation2/serialization/BinarySerializer.cpp index b35c60f719..095ce3ae6d 100644 --- a/source/simulation2/serialization/BinarySerializer.cpp +++ b/source/simulation2/serialization/BinarySerializer.cpp @@ -25,10 +25,7 @@ #include "ps/CLogger.h" #include "scriptinterface/ScriptInterface.h" - -#define signbit std::signbit -#include "js/jsvalue.h" // for JSDOUBLE_IS_INT32 -#undef signbit +#include "scriptinterface/ScriptExtraHeaders.h" // for JSDOUBLE_IS_INT32 CBinarySerializerScriptImpl::CBinarySerializerScriptImpl(ScriptInterface& scriptInterface, ISerializer& serializer) : m_ScriptInterface(scriptInterface), m_Serializer(serializer), m_Rooter(m_ScriptInterface), @@ -188,8 +185,12 @@ void CBinarySerializerScriptImpl::HandleScriptVal(jsval val) void CBinarySerializerScriptImpl::ScriptString(const char* name, JSString* string) { - jschar* chars = JS_GetStringChars(string); - size_t length = JS_GetStringLength(string); + JSContext* cx = m_ScriptInterface.GetContext(); + size_t length; + const jschar* chars = JS_GetStringCharsAndLength(cx, string, &length); + + if (!chars) + throw PSERROR_Serialize_ScriptError("JS_GetStringCharsAndLength failed"); #if BYTE_ORDER != LITTLE_ENDIAN #error TODO: probably need to convert JS strings to little-endian diff --git a/source/simulation2/system/InterfaceScripted.h b/source/simulation2/system/InterfaceScripted.h index 4731c286be..f487701121 100644 --- a/source/simulation2/system/InterfaceScripted.h +++ b/source/simulation2/system/InterfaceScripted.h @@ -24,7 +24,7 @@ #define BEGIN_INTERFACE_WRAPPER(iname) \ JSClass class_ICmp##iname = { \ "ICmp" #iname, JSCLASS_HAS_PRIVATE, \ - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, \ + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, \ JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, \ JSCLASS_NO_OPTIONAL_MEMBERS \ }; \ diff --git a/source/tools/atlas/AtlasObject/AtlasObjectJS.cpp b/source/tools/atlas/AtlasObject/AtlasObjectJS.cpp index 38a5d572b9..37062f81b7 100644 --- a/source/tools/atlas/AtlasObject/AtlasObjectJS.cpp +++ b/source/tools/atlas/AtlasObject/AtlasObjectJS.cpp @@ -71,7 +71,7 @@ static AtSmartPtr ConvertNode(JSContext* cx, jsval node) if (!str) return obj; // error size_t valueLen; - const jschar* valueChars = JS_GetStringCharsAndLength(str, &valueLen); + const jschar* valueChars = JS_GetStringCharsAndLength(cx, str, &valueLen); if (!valueChars) return obj; // error wxString valueWx(reinterpret_cast(valueChars), wxMBConvUTF16(), valueLen*2); @@ -111,9 +111,9 @@ static AtSmartPtr ConvertNode(JSContext* cx, jsval node) continue; // ignore integer properties JSString* name = JSVAL_TO_STRING(val); - size_t len = JS_GetStringLength(name); - jschar* chars = JS_GetStringChars(name); - wxString nameWx(reinterpret_cast(chars), wxMBConvUTF16(), len*2); + size_t len; + const jschar* chars = JS_GetStringCharsAndLength(cx, name, &len); + wxString nameWx(reinterpret_cast(chars), wxMBConvUTF16(), len*2); std::string nameStr(nameWx.ToUTF8().data()); jsval vp; diff --git a/source/tools/atlas/AtlasScript/ScriptInterface.cpp b/source/tools/atlas/AtlasScript/ScriptInterface.cpp index 162a6219bf..51e95cf4bf 100644 --- a/source/tools/atlas/AtlasScript/ScriptInterface.cpp +++ b/source/tools/atlas/AtlasScript/ScriptInterface.cpp @@ -23,12 +23,6 @@ # include #endif -#include "js/jsapi.h" - -#ifdef _WIN32 -# pragma warning(disable: 4996) // avoid complaints about deprecated localtime -#endif - #include "wx/wx.h" #include "GameInterface/Shareable.h" @@ -143,8 +137,11 @@ namespace JSString* ret = JS_ValueToString(cx, v); if (! ret) FAIL("Argument must be convertible to a string"); - jschar* ch = JS_GetStringChars(ret); - out = std::wstring(ch, ch+JS_GetStringLength(ret)); + size_t len; + const jschar* ch = JS_GetStringCharsAndLength(cx, ret, &len); + if (! ch) + FAIL("JS_GetStringsCharsAndLength failed"); // probably out of memory + out = std::wstring(ch, ch + len); return true; } }; @@ -156,10 +153,13 @@ namespace JSString* ret = JS_ValueToString(cx, v); if (! ret) FAIL("Argument must be convertible to a string"); + size_t len = JS_GetStringEncodingLength(cx, ret); + if (len == (size_t)-1) + FAIL("JS_GetStringEncodingLength failed"); char* ch = JS_EncodeString(cx, ret); // chops off high byte of each jschar if (! ch) - FAIL("JS_EncodeString failed"); // out of memory - out = std::string(ch, ch + JS_GetStringLength(ret)); + FAIL("JS_EncodeString failed"); // probably out of memory + out = std::string(ch, ch + len); JS_free(cx, ch); return true; } @@ -170,10 +170,13 @@ namespace static bool Convert(JSContext* cx, jsval v, wxString& out) { JSString* ret = JS_ValueToString(cx, v); + size_t len; if (! ret) FAIL("Argument must be convertible to a string"); - jschar* ch = JS_GetStringChars(ret); - out = wxString((const char*)ch, wxMBConvUTF16(), (size_t)(JS_GetStringLength(ret)*2)); + const jschar* ch = JS_GetStringCharsAndLength(cx, ret, &len); + if (! ch) + FAIL("JS_GetStringsCharsAndLength failed"); // probably out of memory + out = wxString((const char*)ch, wxMBConvUTF16(), len*2); return true; } }; @@ -371,7 +374,7 @@ namespace { JSClass global_class = { "global", JSCLASS_GLOBAL_FLAGS, - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL @@ -416,9 +419,14 @@ namespace JSString* code = JSVAL_TO_STRING(JS_ARGV(cx, vp)[1]); + size_t len; + const jschar* ch = JS_GetStringCharsAndLength(cx, code, &len); + if (!ch) + return JS_FALSE; + jsval rval = JSVAL_VOID; if (!AtlasScriptInterface_impl::LoadScript(cx, - JS_GetStringChars(code), (uintN)JS_GetStringLength(code), + ch, (uintN)len, name.c_str(), &rval)) return JS_FALSE; @@ -465,7 +473,7 @@ AtlasScriptInterface_impl::AtlasScriptInterface_impl() JS_SetVersion(m_cx, JSVERSION_LATEST); - m_glob = JS_NewGlobalObject(m_cx, &global_class); + m_glob = JS_NewCompartmentAndGlobalObject(m_cx, &global_class, NULL); JS_InitStandardClasses(m_cx, m_glob); JS_DefineProperty(m_cx, m_glob, "global", OBJECT_TO_JSVAL(m_glob), NULL, NULL, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT); diff --git a/source/tools/atlas/AtlasScript/ScriptInterface.h b/source/tools/atlas/AtlasScript/ScriptInterface.h index 3358afcf21..7dd4b08416 100644 --- a/source/tools/atlas/AtlasScript/ScriptInterface.h +++ b/source/tools/atlas/AtlasScript/ScriptInterface.h @@ -31,8 +31,39 @@ # define XP_UNIX #endif // (we don't support XP_OS2 or XP_BEOS) +#ifdef __GNUC__ +# define GCC_VERSION (__GNUC__*100 + __GNUC_MINOR__) +#else +# define GCC_VERSION 0 +#endif + +#ifdef _MSC_VER +# define MSC_VERSION _MSC_VER +#else +# define MSC_VERSION 0 +#endif + +// Ignore some harmless warnings triggered by jsapi.h +#if GCC_VERSION >= 402 // (older GCCs don't support this pragma) +# pragma GCC diagnostic ignored "-Wunused-parameter" +# pragma GCC diagnostic ignored "-Wredundant-decls" +#endif +#if MSC_VERSION +# pragma warning(push) +# pragma warning(disable:4480) // "nonstandard extension used: specifying underlying type for enum" +# pragma warning(disable:4100) // "unreferenced formal parameter" +#endif + #include "js/jsapi.h" +#if MSC_VERSION +# pragma warning(pop) +#endif +#if GCC_VERSION >= 402 +# pragma GCC diagnostic warning "-Wunused-parameter" +# pragma GCC diagnostic warning "-Wredundant-decls" +#endif + class wxString; namespace AtlasMessage { struct mWorldCommand; }