1
0
forked from mirrors/0ad

Support new SpiderMonkey API.

wposix: Define int8_t compatibly with SpiderMonkey.
Remove unused camera, lightenv JS interfaces.
Remove most of vector JS interface.
Remove some of the redundant JS string conversion functions.
Remove unneeded vmem, _lodBias functions.
Clean up some formatting.

This was SVN commit r8629.
This commit is contained in:
Ykkrosh
2010-11-16 23:00:52 +00:00
parent 9c521ceb3b
commit bd3bd084c0
83 changed files with 1125 additions and 2149 deletions
+11
View File
@@ -47,4 +47,15 @@ void AutoGCRooter::Trace(JSTracer* trc)
{
JS_CALL_VALUE_TRACER(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)
{
jsval val = JSVAL_VOID;
JS_IdToValue(m_ScriptInterface.GetContext(), m_IdArrays[i]->vector[j], &val);
JS_CALL_VALUE_TRACER(trc, val, "AutoGCRooter id array");
}
}
}
+2 -24
View File
@@ -22,30 +22,6 @@
#include "js/jsapi.h"
// Exception-safety and GC-safety wrapper for JSIdArray
// (TODO: it'd be nicer to use the existing js::AutoIdArray but currently that
// requires a load of semi-private SpiderMonkey headers that cause a load of compile warnings)
class IdArrayWrapper
{
JSContext* m_cx;
JSIdArray* m_ida;
public:
IdArrayWrapper(JSContext* cx, JSIdArray* ida) :
m_cx(cx), m_ida(ida)
{
for (jsint i = 0; i < m_ida->length; ++i)
if (!JS_AddRoot(m_cx, &m_ida->vector[i]))
debug_warn(L"JS_AddRoot failed");
}
~IdArrayWrapper()
{
for (jsint i = 0; i < m_ida->length; ++i)
if (!JS_RemoveRoot(m_cx, &m_ida->vector[i]))
debug_warn(L"JS_RemoveRoot failed");
JS_DestroyIdArray(m_cx, m_ida);
}
};
/**
* Helper for rooting large groups of script values.
* Construct this object, push values into it, and they will all be rooted until this
@@ -61,6 +37,7 @@ public:
void Push(JSObject* obj) { m_Objects.push_back(obj); }
void Push(jsval val) { m_Vals.push_back(val); }
void Push(JSIdArray* ida) { m_IdArrays.push_back(ida); }
void Trace(JSTracer* trc);
private:
@@ -69,6 +46,7 @@ private:
std::vector<JSObject*> m_Objects;
std::vector<jsval> m_Vals;
std::vector<JSIdArray*> m_IdArrays;
// TODO: add vectors of other value types
};
+31 -20
View File
@@ -21,18 +21,19 @@
#include "ps/utf16string.h"
#include "ps/CLogger.h"
#include "ps/CStr.h"
#include "js/jsapi.h"
#define FAIL(msg) STMT(JS_ReportError(cx, msg); return false)
// Implicit type conversions often hide bugs, so warn about them
#define WARN_IF_NOT(c) STMT(if (!(c)) { JS_ReportWarning(cx, "Script value conversion check failed: %s", #c); })
#define WARN_IF_NOT(c, v) STMT(if (!(c)) { JS_ReportWarning(cx, "Script value conversion check failed: %s (got type %s)", #c, JS_GetTypeName(cx, JS_TypeOfValue(cx, v))); })
template<> bool ScriptInterface::FromJSVal<bool>(JSContext* cx, jsval v, bool& out)
{
JSBool ret;
WARN_IF_NOT(JSVAL_IS_BOOLEAN(v));
WARN_IF_NOT(JSVAL_IS_BOOLEAN(v), v);
if (!JS_ValueToBoolean(cx, v, &ret))
return false;
out = (ret ? true : false);
@@ -42,7 +43,7 @@ template<> bool ScriptInterface::FromJSVal<bool>(JSContext* cx, jsval v, bool& o
template<> bool ScriptInterface::FromJSVal<float>(JSContext* cx, jsval v, float& out)
{
jsdouble ret;
WARN_IF_NOT(JSVAL_IS_NUMBER(v));
WARN_IF_NOT(JSVAL_IS_NUMBER(v), v);
if (!JS_ValueToNumber(cx, v, &ret))
return false;
out = ret;
@@ -52,7 +53,7 @@ template<> bool ScriptInterface::FromJSVal<float>(JSContext* cx, jsval v, float&
template<> bool ScriptInterface::FromJSVal<double>(JSContext* cx, jsval v, double& out)
{
jsdouble ret;
WARN_IF_NOT(JSVAL_IS_NUMBER(v));
WARN_IF_NOT(JSVAL_IS_NUMBER(v), v);
if (!JS_ValueToNumber(cx, v, &ret))
return false;
out = ret;
@@ -62,7 +63,7 @@ template<> bool ScriptInterface::FromJSVal<double>(JSContext* cx, jsval v, doubl
template<> bool ScriptInterface::FromJSVal<i32>(JSContext* cx, jsval v, i32& out)
{
int32 ret;
WARN_IF_NOT(JSVAL_IS_INT(v));
WARN_IF_NOT(JSVAL_IS_NUMBER(v), v);
if (!JS_ValueToECMAInt32(cx, v, &ret))
return false;
out = ret;
@@ -72,7 +73,7 @@ template<> bool ScriptInterface::FromJSVal<i32>(JSContext* cx, jsval v, i32& out
template<> bool ScriptInterface::FromJSVal<u32>(JSContext* cx, jsval v, u32& out)
{
uint32 ret;
WARN_IF_NOT(JSVAL_IS_INT(v));
WARN_IF_NOT(JSVAL_IS_NUMBER(v), v);
if (!JS_ValueToECMAUint32(cx, v, &ret))
return false;
out = ret;
@@ -94,7 +95,7 @@ template<> bool ScriptInterface::FromJSVal<CScriptValRooted>(JSContext* cx, jsva
template<> bool ScriptInterface::FromJSVal<std::wstring>(JSContext* cx, jsval v, std::wstring& out)
{
WARN_IF_NOT(JSVAL_IS_STRING(v));
WARN_IF_NOT(JSVAL_IS_STRING(v) || JSVAL_IS_NUMBER(v), v); // allow implicit number conversions
JSString* ret = JS_ValueToString(cx, v);
if (!ret)
FAIL("Argument must be convertible to a string");
@@ -105,15 +106,15 @@ template<> bool ScriptInterface::FromJSVal<std::wstring>(JSContext* cx, jsval v,
template<> bool ScriptInterface::FromJSVal<std::string>(JSContext* cx, jsval v, std::string& out)
{
WARN_IF_NOT(JSVAL_IS_STRING(v));
WARN_IF_NOT(JSVAL_IS_STRING(v) || JSVAL_IS_NUMBER(v), v); // allow implicit number conversions
JSString* ret = JS_ValueToString(cx, v);
if (!ret)
FAIL("Argument must be convertible to a string");
char* ch = JS_GetStringBytes(ret);
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));
// TODO: if JS_GetStringBytes fails it'll return a zero-length string
// and we'll overflow its bounds by using JS_GetStringLength - should
// use one of the new SpiderMonkey 1.8 functions instead
JS_free(cx, ch);
return true;
}
@@ -139,13 +140,10 @@ template<> jsval ScriptInterface::ToJSVal<double>(JSContext* cx, const double& v
return rval;
}
template<> jsval ScriptInterface::ToJSVal<i32>(JSContext* cx, const i32& val)
template<> jsval ScriptInterface::ToJSVal<i32>(JSContext* UNUSED(cx), const i32& val)
{
if (INT_FITS_IN_JSVAL(val))
return INT_TO_JSVAL(val);
jsval rval = JSVAL_VOID;
JS_NewNumberValue(cx, val, &rval); // ignore return value
return rval;
cassert(JSVAL_INT_BITS == 32);
return INT_TO_JSVAL(val);
}
template<> jsval ScriptInterface::ToJSVal<u32>(JSContext* cx, const u32& val)
@@ -185,6 +183,11 @@ template<> jsval ScriptInterface::ToJSVal<std::string>(JSContext* cx, const std:
return JSVAL_VOID;
}
template<> jsval ScriptInterface::ToJSVal<const wchar_t*>(JSContext* cx, const wchar_t* const& val)
{
return ToJSVal(cx, std::wstring(val));
}
template<> jsval ScriptInterface::ToJSVal<const char*>(JSContext* cx, const char* const& val)
{
JSString* str = JS_NewStringCopyZ(cx, val);
@@ -193,6 +196,16 @@ template<> jsval ScriptInterface::ToJSVal<const char*>(JSContext* cx, const char
return JSVAL_VOID;
}
template<> jsval ScriptInterface::ToJSVal<CStrW>(JSContext* cx, const CStrW& val)
{
return ToJSVal(cx, static_cast<const std::wstring&>(val));
}
template<> jsval ScriptInterface::ToJSVal<CStr8>(JSContext* cx, const CStr8& val)
{
return ToJSVal(cx, static_cast<const std::string&>(val));
}
////////////////////////////////////////////////////////////////
// Compound types:
@@ -201,13 +214,11 @@ template<typename T> static jsval ToJSVal_vector(JSContext* cx, const std::vecto
JSObject* obj = JS_NewArrayObject(cx, 0, NULL);
if (!obj)
return JSVAL_VOID;
JS_AddRoot(cx, &obj);
for (size_t i = 0; i < val.size(); ++i)
{
jsval el = ScriptInterface::ToJSVal<T>(cx, val[i]);
JS_SetElement(cx, obj, (jsint)i, &el);
}
JS_RemoveRoot(cx, &obj);
return OBJECT_TO_JSVAL(obj);
}
+38 -69
View File
@@ -40,7 +40,9 @@ const int RUNTIME_SIZE = 16 * 1024 * 1024; // TODO: how much memory is needed?
const int STACK_CHUNK_SIZE = 8192;
#if ENABLE_SCRIPT_PROFILING
#include "js/jsdbgapi.h"
# define signbit std::signbit
# include "js/jsdbgapi.h"
# undef signbit
#endif
////////////////////////////////////////////////////////////////
@@ -49,7 +51,7 @@ struct ScriptInterface_impl
{
ScriptInterface_impl(const char* nativeScopeName, JSContext* cx);
~ScriptInterface_impl();
void Register(const char* name, JSFastNative fptr, uintN nargs);
void Register(const char* name, JSNative fptr, uintN nargs);
JSRuntime* m_rt; // NULL if m_cx is shared; non-NULL if we own m_cx
JSContext* m_cx;
@@ -109,7 +111,7 @@ void ErrorReporter(JSContext* cx, const char* message, JSErrorReport* report)
LOGERROR(L"%hs", msg.str().c_str());
// When running under Valgrind, print more information in the error message
VALGRIND_PRINTF_BACKTRACE("->");
// VALGRIND_PRINTF_BACKTRACE("->");
}
// Functions in the global namespace:
@@ -276,7 +278,11 @@ ScriptInterface_impl::ScriptInterface_impl(const char* nativeScopeName, JSContex
JS_SetOptions(m_cx, JSOPTION_STRICT // "warn on dubious practice"
| JSOPTION_XML // "ECMAScript for XML support: parse <!-- --> as a token"
| JSOPTION_VAROBJFIX // "recommended" (fixes variable scoping)
// Enable all the JIT features:
// | JSOPTION_JIT
// | JSOPTION_METHODJIT
// | JSOPTION_PROFILING
);
JS_SetVersion(m_cx, JSVERSION_LATEST);
@@ -286,7 +292,7 @@ ScriptInterface_impl::ScriptInterface_impl(const char* nativeScopeName, JSContex
// Threadsafe SpiderMonkey requires that we have a request before doing anything much
JS_BeginRequest(m_cx);
m_glob = JS_NewObject(m_cx, &global_class, NULL, NULL);
m_glob = JS_NewGlobalObject(m_cx, &global_class);
ok = JS_InitStandardClasses(m_cx, m_glob);
JS_DefineProperty(m_cx, m_glob, "global", OBJECT_TO_JSVAL(m_glob), NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY
@@ -296,10 +302,10 @@ ScriptInterface_impl::ScriptInterface_impl(const char* nativeScopeName, JSContex
m_nativeScope = JS_DefineObject(m_cx, m_glob, nativeScopeName, NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY
| JSPROP_PERMANENT);
JS_DefineFunction(m_cx, m_glob, "print", (JSNative)::print, 0, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSFUN_FAST_NATIVE);
JS_DefineFunction(m_cx, m_glob, "log", (JSNative)::logmsg, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSFUN_FAST_NATIVE);
JS_DefineFunction(m_cx, m_glob, "warn", (JSNative)::warn, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSFUN_FAST_NATIVE);
JS_DefineFunction(m_cx, m_glob, "error", (JSNative)::error, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSFUN_FAST_NATIVE);
JS_DefineFunction(m_cx, m_glob, "print", ::print, 0, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
JS_DefineFunction(m_cx, m_glob, "log", ::logmsg, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
JS_DefineFunction(m_cx, m_glob, "warn", ::warn, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
JS_DefineFunction(m_cx, m_glob, "error", ::error, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
}
ScriptInterface_impl::~ScriptInterface_impl()
@@ -312,9 +318,9 @@ ScriptInterface_impl::~ScriptInterface_impl()
}
}
void ScriptInterface_impl::Register(const char* name, JSFastNative fptr, uintN nargs)
void ScriptInterface_impl::Register(const char* name, JSNative fptr, uintN nargs)
{
JS_DefineFunction(m_cx, m_nativeScope, name, reinterpret_cast<JSNative>(fptr), nargs, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSFUN_FAST_NATIVE);
JS_DefineFunction(m_cx, m_nativeScope, name, fptr, nargs, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
}
ScriptInterface::ScriptInterface(const char* nativeScopeName, const char* debugName) :
@@ -361,8 +367,8 @@ void ScriptInterface::ReplaceNondeterministicFunctions(boost::rand48& rng)
return;
}
JSFunction* random = JS_DefineFunction(m->m_cx, JSVAL_TO_OBJECT(math), "random", (JSNative)Math_random, 0,
JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSFUN_FAST_NATIVE);
JSFunction* random = JS_DefineFunction(m->m_cx, JSVAL_TO_OBJECT(math), "random", Math_random, 0,
JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
if (!random)
{
LOGERROR(L"ReplaceNondeterministicFunctions: failed to replace Math.random");
@@ -372,7 +378,7 @@ void ScriptInterface::ReplaceNondeterministicFunctions(boost::rand48& rng)
JS_SetReservedSlot(m->m_cx, JS_GetFunctionObject(random), 0, PRIVATE_TO_JSVAL(&rng));
}
void ScriptInterface::Register(const char* name, JSFastNative fptr, size_t nargs)
void ScriptInterface::Register(const char* name, JSNative fptr, size_t nargs)
{
m->Register(name, fptr, (uintN)nargs);
}
@@ -387,16 +393,6 @@ JSRuntime* ScriptInterface::GetRuntime() const
return m->m_rt;
}
bool ScriptInterface::AddRoot(void* ptr, const char* name)
{
return JS_AddNamedRoot(m->m_cx, ptr, name) ? true : false;
}
bool ScriptInterface::RemoveRoot(void* ptr)
{
return JS_RemoveRoot(m->m_cx, ptr) ? true : false;
}
AutoGCRooter* ScriptInterface::ReplaceAutoGCRooter(AutoGCRooter* rooter)
{
debug_assert(m->m_rt); // this class must own the runtime, else the rooter won't work
@@ -405,30 +401,6 @@ AutoGCRooter* ScriptInterface::ReplaceAutoGCRooter(AutoGCRooter* rooter)
return ret;
}
ScriptInterface::LocalRootScope::LocalRootScope(JSContext* cx) :
m_cx(cx)
{
m_OK = JS_EnterLocalRootScope(m_cx) ? true : false;
}
ScriptInterface::LocalRootScope::~LocalRootScope()
{
if (m_OK)
JS_LeaveLocalRootScope(m_cx);
}
bool ScriptInterface::LocalRootScope::OK()
{
return m_OK;
}
void ScriptInterface::LocalRootScope::LeaveWithResult(jsval val)
{
if (m_OK)
JS_LeaveLocalRootScopeWithResult(m_cx, val);
m_OK = false;
}
jsval ScriptInterface::CallConstructor(jsval ctor)
{
@@ -494,19 +466,14 @@ bool ScriptInterface::CallFunction_(jsval val, const char* name, size_t argc, js
JSObject* obj;
if (!JS_ValueToObject(m->m_cx, val, &obj) || obj == NULL)
return false;
JS_AddRoot(m->m_cx, &obj);
// Check that the named function actually exists, to avoid ugly JS error reports
// when calling an undefined value
JSBool found;
if (!JS_HasProperty(m->m_cx, obj, name, &found) || !found)
{
JS_RemoveRoot(m->m_cx, &obj);
return false;
}
JSBool ok = JS_CallFunctionName(m->m_cx, obj, name, (uintN)argc, argv, &ret);
JS_RemoveRoot(m->m_cx, &obj);
return ok ? true : false;
}
@@ -516,6 +483,11 @@ jsval ScriptInterface::GetGlobalObject()
return OBJECT_TO_JSVAL(JS_GetGlobalObject(m->m_cx));
}
JSClass* ScriptInterface::GetGlobalClass()
{
return &global_class;
}
bool ScriptInterface::SetGlobal_(const char* name, jsval value, bool replace)
{
if (!replace)
@@ -577,8 +549,6 @@ bool ScriptInterface::HasProperty(jsval obj, const char* name)
bool ScriptInterface::EnumeratePropertyNamesWithPrefix(jsval obj, const char* prefix, std::vector<std::string>& out)
{
LOCAL_ROOT_SCOPE;
utf16string prefix16 (prefix, prefix+strlen(prefix));
if (! JSVAL_IS_OBJECT(obj))
@@ -638,10 +608,8 @@ bool ScriptInterface::LoadScript(const std::wstring& filename, const std::wstrin
if (!func)
return false;
JS_AddRoot(m->m_cx, &func); // TODO: do we need to root this?
jsval scriptRval;
JSBool ok = JS_CallFunction(m->m_cx, NULL, func, 0, NULL, &scriptRval);
JS_RemoveRoot(m->m_cx, &func);
return ok ? true : false;
}
@@ -687,7 +655,7 @@ CScriptValRooted ScriptInterface::ParseJSON(const utf16string& string)
return CScriptValRooted();
}
if (!JS_ConsumeJSONText(m->m_cx, parser, string.c_str(), (uint32)string.size()))
if (!JS_ConsumeJSONText(m->m_cx, parser, reinterpret_cast<const jschar*>(string.c_str()), (uint32)string.size()))
{
LOGERROR(L"ParseJSON failed to consume");
return CScriptValRooted();
@@ -786,7 +754,7 @@ class ValueCloner
{
public:
ValueCloner(ScriptInterface& from, ScriptInterface& to) :
cxFrom(from.GetContext()), cxTo(to.GetContext()), m_RooterFrom(from), m_RooterTo(to)
scriptInterfaceFrom(from), cxFrom(from.GetContext()), cxTo(to.GetContext()), m_RooterFrom(from), m_RooterTo(to)
{
}
@@ -796,7 +764,7 @@ public:
if (!JSVAL_IS_GCTHING(val) || JSVAL_IS_NULL(val))
return val;
std::map<jsval, jsval>::iterator it = m_Mapping.find(val);
std::map<void*, jsval>::iterator it = m_Mapping.find(JSVAL_TO_GCTHING(val));
if (it != m_Mapping.end())
return it->second;
@@ -815,8 +783,7 @@ private:
if (JSVAL_IS_DOUBLE(val))
{
jsval rval;
CLONE_REQUIRE(JS_NewNumberValue(cxTo, *JSVAL_TO_DOUBLE(val), &rval), L"JS_NewNumberValue");
m_Mapping[val] = rval;
CLONE_REQUIRE(JS_NewNumberValue(cxTo, JSVAL_TO_DOUBLE(val), &rval), L"JS_NewNumberValue");
m_RooterTo.Push(rval);
return rval;
}
@@ -826,7 +793,7 @@ private:
JSString* str = JS_NewUCStringCopyN(cxTo, JS_GetStringChars(JSVAL_TO_STRING(val)), JS_GetStringLength(JSVAL_TO_STRING(val)));
CLONE_REQUIRE(str, L"JS_NewUCStringCopyN");
jsval rval = STRING_TO_JSVAL(str);
m_Mapping[val] = rval;
m_Mapping[JSVAL_TO_GCTHING(val)] = rval;
m_RooterTo.Push(rval);
return rval;
}
@@ -847,17 +814,18 @@ private:
CLONE_REQUIRE(newObj, L"JS_NewObject");
}
m_Mapping[val] = OBJECT_TO_JSVAL(newObj);
m_Mapping[JSVAL_TO_GCTHING(val)] = OBJECT_TO_JSVAL(newObj);
m_RooterTo.Push(newObj);
JSIdArray* ida = JS_Enumerate(cxFrom, JSVAL_TO_OBJECT(val));
CLONE_REQUIRE(ida, L"JS_Enumerate");
AutoJSIdArray ida (cxFrom, JS_Enumerate(cxFrom, JSVAL_TO_OBJECT(val)));
CLONE_REQUIRE(ida.get(), L"JS_Enumerate");
IdArrayWrapper idaWrapper(cxFrom, ida);
AutoGCRooter idaRooter(scriptInterfaceFrom);
idaRooter.Push(ida.get());
for (jsint i = 0; i < ida->length; ++i)
for (size_t i = 0; i < ida.length(); ++i)
{
jsid id = ida->vector[i];
jsid id = ida[i];
jsval idval, propval;
CLONE_REQUIRE(JS_IdToValue(cxFrom, id, &idval), L"JS_IdToValue");
CLONE_REQUIRE(JS_GetPropertyById(cxFrom, JSVAL_TO_OBJECT(val), id, &propval), L"JS_GetPropertyById");
@@ -884,9 +852,10 @@ private:
return OBJECT_TO_JSVAL(newObj);
}
ScriptInterface& scriptInterfaceFrom;
JSContext* cxFrom;
JSContext* cxTo;
std::map<jsval, jsval> m_Mapping;
std::map<void*, jsval> m_Mapping;
AutoGCRooter m_RooterFrom;
AutoGCRooter m_RooterTo;
};
+5 -39
View File
@@ -18,10 +18,6 @@
#ifndef INCLUDED_SCRIPTINTERFACE
#define INCLUDED_SCRIPTINTERFACE
// XXX: This is largely copied from tools/atlas/AtlasScript
// Duplication is bad - they should use a shared version of the common code,
// and just add their own extensions individually
#include <memory>
#include <vector>
#include <string>
@@ -144,6 +140,8 @@ public:
jsval GetGlobalObject();
JSClass* GetGlobalClass();
/**
* Set the named property on the global object.
* If @p replace is true, an existing property will be overwritten; otherwise attempts
@@ -223,9 +221,6 @@ public:
*/
template<typename T> static jsval ToJSVal(JSContext* cx, T const& val);
bool AddRoot(void* ptr, const char* name);
bool RemoveRoot(void* ptr);
AutoGCRooter* ReplaceAutoGCRooter(AutoGCRooter* rooter);
/**
@@ -233,26 +228,6 @@ public:
*/
void DumpHeap();
// Helper class for automatically rooting values
class LocalRootScope
{
JSContext* m_cx;
bool m_OK;
public:
// Tries to enter local root scope, so newly created
// values won't be GCed. This might fail, so check OK()
LocalRootScope(JSContext* cx);
// Returns true if the local root scope was successfully entered
bool OK();
// Leaves the local root scope, but keeps the given return value rooted
void LeaveWithResult(jsval val);
// Leaves the local root scope
~LocalRootScope();
private:
LocalRootScope& operator=(const LocalRootScope&);
};
#define LOCAL_ROOT_SCOPE LocalRootScope scope(GetContext()); if (! scope.OK()) return false
void MaybeGC();
private:
@@ -266,7 +241,7 @@ private:
static JSClass* GetClass(JSContext* cx, JSObject* obj);
static void* GetPrivate(JSContext* cx, JSObject* obj);
void Register(const char* name, JSFastNative fptr, size_t nargs);
void Register(const char* name, JSNative fptr, size_t nargs);
std::auto_ptr<ScriptInterface_impl> m;
// The nasty macro/template bits are split into a separate file so you don't have to look at them
@@ -278,10 +253,10 @@ public:
// void RegisterFunction(const char* functionName);
//
// template <R, T0..., TR (*fptr) (void* cbdata, T0...)>
// static JSFastNative call;
// static JSNative call;
//
// template <R, T0..., JSClass*, TC, TR (TC:*fptr) (T0...)>
// static JSFastNative callMethod;
// static JSNative callMethod;
//
// template <dummy, T0...>
// static size_t nargs();
@@ -293,7 +268,6 @@ public:
template<typename R>
bool ScriptInterface::CallFunction(jsval val, const char* name, R& ret)
{
LOCAL_ROOT_SCOPE;
jsval jsRet;
bool ok = CallFunction_(val, name, 0, NULL, jsRet);
if (!ok)
@@ -304,7 +278,6 @@ bool ScriptInterface::CallFunction(jsval val, const char* name, R& ret)
template<typename T0>
bool ScriptInterface::CallFunctionVoid(jsval val, const char* name, const T0& a0)
{
LOCAL_ROOT_SCOPE;
jsval jsRet;
jsval argv[1];
argv[0] = ToJSVal(GetContext(), a0);
@@ -314,7 +287,6 @@ bool ScriptInterface::CallFunctionVoid(jsval val, const char* name, const T0& a0
template<typename T0, typename T1>
bool ScriptInterface::CallFunctionVoid(jsval val, const char* name, const T0& a0, const T1& a1)
{
LOCAL_ROOT_SCOPE;
jsval jsRet;
jsval argv[2];
argv[0] = ToJSVal(GetContext(), a0);
@@ -325,7 +297,6 @@ bool ScriptInterface::CallFunctionVoid(jsval val, const char* name, const T0& a0
template<typename T0, typename T1, typename T2>
bool ScriptInterface::CallFunctionVoid(jsval val, const char* name, const T0& a0, const T1& a1, const T2& a2)
{
LOCAL_ROOT_SCOPE;
jsval jsRet;
jsval argv[3];
argv[0] = ToJSVal(GetContext(), a0);
@@ -337,7 +308,6 @@ bool ScriptInterface::CallFunctionVoid(jsval val, const char* name, const T0& a0
template<typename T0, typename R>
bool ScriptInterface::CallFunction(jsval val, const char* name, const T0& a0, R& ret)
{
LOCAL_ROOT_SCOPE;
jsval jsRet;
jsval argv[1];
argv[0] = ToJSVal(GetContext(), a0);
@@ -350,7 +320,6 @@ bool ScriptInterface::CallFunction(jsval val, const char* name, const T0& a0, R&
template<typename T0, typename T1, typename R>
bool ScriptInterface::CallFunction(jsval val, const char* name, const T0& a0, const T1& a1, R& ret)
{
LOCAL_ROOT_SCOPE;
jsval jsRet;
jsval argv[2];
argv[0] = ToJSVal(GetContext(), a0);
@@ -364,7 +333,6 @@ bool ScriptInterface::CallFunction(jsval val, const char* name, const T0& a0, co
template<typename T0, typename T1, typename T2, typename R>
bool ScriptInterface::CallFunction(jsval val, const char* name, const T0& a0, const T1& a1, const T2& a2, R& ret)
{
LOCAL_ROOT_SCOPE;
jsval jsRet;
jsval argv[3];
argv[0] = ToJSVal(GetContext(), a0);
@@ -379,14 +347,12 @@ 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)
{
LOCAL_ROOT_SCOPE;
return SetGlobal_(name, ToJSVal(GetContext(), value), replace);
}
template<typename T>
bool ScriptInterface::SetProperty(jsval obj, const char* name, const T& value, bool readonly)
{
LOCAL_ROOT_SCOPE;
return SetProperty_(obj, name, ToJSVal(GetContext(), value), readonly);
}
+9 -3
View File
@@ -1,4 +1,4 @@
/* Copyright (C) 2009 Wildfire Games.
/* Copyright (C) 2010 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -20,13 +20,19 @@
#ifdef _WIN32
# define XP_WIN
# define WIN32 // SpiderMonkey expects this
// 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
#else
# define XP_UNIX
#endif
// (we don't support XP_OS2 or XP_BEOS)
#include "js/jspubtd.h"
#include "js/jsversion.h"
#include "js/jsapi.h"
#if JS_VERSION != 185
#error Your compiler is trying to use an incorrect version of the SpiderMonkey library.
+40 -5
View File
@@ -1,4 +1,4 @@
/* Copyright (C) 2009 Wildfire Games.
/* Copyright (C) 2010 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -24,21 +24,21 @@
struct Unrooter
{
Unrooter(JSContext* cx) : cx(cx) { }
void operator()(jsval* p) { JS_RemoveRoot(cx, p); delete p; }
void operator()(jsval* p) { JS_RemoveValueRoot(cx, p); delete p; }
JSContext* cx;
};
CScriptValRooted::CScriptValRooted(JSContext* cx, jsval val)
{
jsval* p = new jsval(val);
JS_AddNamedRoot(cx, p, "CScriptValRooted");
JS_AddNamedValueRoot(cx, p, "CScriptValRooted");
m_Val = boost::shared_ptr<jsval>(p, Unrooter(cx));
}
CScriptValRooted::CScriptValRooted(JSContext* cx, CScriptVal val)
{
jsval* p = new jsval(val.get());
JS_AddNamedRoot(cx, p, "CScriptValRooted");
JS_AddNamedValueRoot(cx, p, "CScriptValRooted");
m_Val = boost::shared_ptr<jsval>(p, Unrooter(cx));
}
@@ -49,12 +49,47 @@ jsval CScriptValRooted::get() const
return *m_Val;
}
jsval& CScriptValRooted::getRef() const
{
debug_assert(m_Val);
return *m_Val;
}
bool CScriptValRooted::undefined() const
{
return (!m_Val || *m_Val == JSVAL_VOID);
return (!m_Val || JSVAL_IS_VOID(*m_Val));
}
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
{
debug_assert(m_IdArray);
return m_IdArray->length;
}
jsid AutoJSIdArray::operator[](size_t i) const
{
debug_assert(m_IdArray);
debug_assert(i < (size_t)m_IdArray->length);
return m_IdArray->vector[i];
}
+37 -3
View File
@@ -1,4 +1,4 @@
/* Copyright (C) 2009 Wildfire Games.
/* Copyright (C) 2010 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -29,10 +29,10 @@
class CScriptVal
{
public:
CScriptVal() : m_Val(0) { }
CScriptVal() : m_Val(JSVAL_VOID) { }
CScriptVal(jsval val) : m_Val(val) { }
jsval get() const { return m_Val; }
const jsval& get() const { return m_Val; }
private:
jsval m_Val;
@@ -45,14 +45,48 @@ public:
CScriptValRooted(JSContext* cx, jsval val);
CScriptValRooted(JSContext* cx, CScriptVal val);
/**
* Returns the current value (or JSVAL_VOID if uninitialised).
*/
jsval get() const;
/**
* Returns reference to the current value.
* Fails if the value is not yet initialised.
*/
jsval& getRef() const;
/**
* Returns whether the value is uninitialised or is JSVAL_VOID.
*/
bool undefined() const;
/**
* Returns whether the value is uninitialised.
*/
bool uninitialised() const;
private:
boost::shared_ptr<jsval> m_Val;
};
/**
* 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
@@ -34,7 +34,6 @@ class TestScriptConversions : public CxxTest::TestSuite
JSContext* cx = script.GetContext();
jsval v1 = ScriptInterface::ToJSVal(cx, value);
JS_AddRoot(cx, &v1);
// 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.
@@ -42,8 +41,6 @@ class TestScriptConversions : public CxxTest::TestSuite
TS_ASSERT(script.CallFunction(OBJECT_TO_JSVAL(JS_GetGlobalObject(cx)), "uneval", CScriptVal(v1), source));
TS_ASSERT_STR_EQUALS(source, expected);
JS_RemoveRoot(cx, &v1);
}
template <typename T>
@@ -53,7 +50,6 @@ class TestScriptConversions : public CxxTest::TestSuite
JSContext* cx = script.GetContext();
jsval v1 = ScriptInterface::ToJSVal(cx, value);
JS_AddRoot(cx, &v1);
std::string source;
TS_ASSERT(script.CallFunction(OBJECT_TO_JSVAL(JS_GetGlobalObject(cx)), "uneval", CScriptVal(v1), source));
@@ -64,8 +60,6 @@ class TestScriptConversions : public CxxTest::TestSuite
T v2 = T();
TS_ASSERT(ScriptInterface::FromJSVal(script.GetContext(), v1, v2));
TS_ASSERT_EQUALS(value, v2);
JS_RemoveRoot(cx, &v1);
}
public:
@@ -132,18 +126,16 @@ public:
TS_ASSERT(JSVAL_IS_INT(ScriptInterface::ToJSVal<i32>(cx, 0)));
TS_ASSERT(JSVAL_IS_INT(ScriptInterface::ToJSVal<i32>(cx, 1073741822))); // JSVAL_INT_MAX-1
TS_ASSERT(JSVAL_IS_INT(ScriptInterface::ToJSVal<i32>(cx, 1073741823))); // JSVAL_INT_MAX
TS_ASSERT(JSVAL_IS_DOUBLE(ScriptInterface::ToJSVal<i32>(cx, 1073741824))); // JSVAL_INT_MAX+1
TS_ASSERT(JSVAL_IS_INT(ScriptInterface::ToJSVal<i32>(cx, -1073741823))); // JSVAL_INT_MIN+1
TS_ASSERT(JSVAL_IS_INT(ScriptInterface::ToJSVal<i32>(cx, -1073741824))); // JSVAL_INT_MIN
TS_ASSERT(JSVAL_IS_DOUBLE(ScriptInterface::ToJSVal<i32>(cx, -1073741825))); // JSVAL_INT_MIN-1
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)2147483648))); // JSVAL_INT_MIN
TS_ASSERT(JSVAL_IS_INT(ScriptInterface::ToJSVal<u32>(cx, 0)));
TS_ASSERT(JSVAL_IS_INT(ScriptInterface::ToJSVal<u32>(cx, 1073741822))); // JSVAL_INT_MAX-1
TS_ASSERT(JSVAL_IS_INT(ScriptInterface::ToJSVal<u32>(cx, 1073741823))); // JSVAL_INT_MAX
TS_ASSERT(JSVAL_IS_DOUBLE(ScriptInterface::ToJSVal<u32>(cx, 1073741824))); // JSVAL_INT_MAX+1
TS_ASSERT(JSVAL_IS_INT(ScriptInterface::ToJSVal<u32>(cx, 2147483646))); // JSVAL_INT_MAX-1
TS_ASSERT(JSVAL_IS_INT(ScriptInterface::ToJSVal<u32>(cx, 2147483647))); // JSVAL_INT_MAX
TS_ASSERT(JSVAL_IS_DOUBLE(ScriptInterface::ToJSVal<u32>(cx, 2147483648))); // JSVAL_INT_MAX+1
}
void test_nonfinite()
@@ -136,7 +136,7 @@ public:
TS_ASSERT(script.Eval(input.c_str(), val));
std::string stringified = script.StringifyJSON(val.get());
TS_ASSERT_STR_EQUALS(stringified, "{\n \"x\":1,\n \"z\":[2,\n \"3\xE2\x98\xBA\xEF\xBF\xBD\"\n ],\n \"y\":true\n}");
TS_ASSERT_STR_EQUALS(stringified, "{\n \"x\": 1,\n \"z\": [\n 2,\n \"3\xE2\x98\xBA\xEF\xBF\xBD\"\n ],\n \"y\": true\n}");
val = script.ParseJSON(stringified);
TS_ASSERT_WSTR_EQUALS(script.ToString(val.get()), L"({x:1, z:[2, \"3\\u263A\\uFFFD\"], y:true})");