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:
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
@@ -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})");
|
||||
|
||||
Reference in New Issue
Block a user