1
0
forked from mirrors/0ad

Improve JS Exception handling.

- Check for pending exceptions after function calls and script
executions.
- Call LOGERROR instead of JS_ReportError when there is a conversion
error in FromJSVal, since that can only be called from C++ (where JS
errors don't really make sense). Instead, C++ callers of FromJSVal
should handle the failure and, themselves, either report an error or
simply do something else.
- Wrap JS_ReportError since that makes updating it later easier.

This isn't a systematical fix since ToJSVal also ought return a boolean
for failures, and we probably should trigger errors instead of warnings
on 'implicit' conversions, rather a preparation diff.

Part of the SM52 migration, stage: SM45 compatible (actually SM52
incompatible, too).

Based on a patch by: Itms
Comments by: Vladislavbelov, Stan`
Refs #742, #4893

Differential Revision: https://code.wildfiregames.com/D3093
This was SVN commit r24187.
This commit is contained in:
wraitii
2020-11-15 18:29:17 +00:00
parent 88bc973530
commit 25490bfec3
79 changed files with 810 additions and 667 deletions
+16 -16
View File
@@ -69,7 +69,7 @@ template <typename R>
struct ScriptInterface_NativeWrapper
{
template<typename F, typename... Ts>
static void call(const ScriptInterface::Request& rq, JS::MutableHandleValue rval, F fptr, Ts... params)
static void call(const ScriptRequest& rq, JS::MutableHandleValue rval, F fptr, Ts... params)
{
ScriptInterface::AssignOrToJSValUnrooted<R>(rq, rval, fptr(ScriptInterface::GetScriptInterfaceAndCBData(rq.cx), params...));
}
@@ -80,7 +80,7 @@ template <>
struct ScriptInterface_NativeWrapper<void>
{
template<typename F, typename... Ts>
static void call(const ScriptInterface::Request& rq, JS::MutableHandleValue UNUSED(rval), F fptr, Ts... params)
static void call(const ScriptRequest& rq, JS::MutableHandleValue UNUSED(rval), F fptr, Ts... params)
{
fptr(ScriptInterface::GetScriptInterfaceAndCBData(rq.cx), params...);
}
@@ -92,7 +92,7 @@ template <typename R, typename TC>
struct ScriptInterface_NativeMethodWrapper
{
template<typename F, typename... Ts>
static void call(const ScriptInterface::Request& rq, JS::MutableHandleValue rval, TC* c, F fptr, Ts... params)
static void call(const ScriptRequest& rq, JS::MutableHandleValue rval, TC* c, F fptr, Ts... params)
{
ScriptInterface::AssignOrToJSValUnrooted<R>(rq, rval, (c->*fptr)(params...));
}
@@ -102,7 +102,7 @@ template <typename TC>
struct ScriptInterface_NativeMethodWrapper<void, TC>
{
template<typename F, typename... Ts>
static void call(const ScriptInterface::Request& UNUSED(rq), JS::MutableHandleValue UNUSED(rval), TC* c, F fptr, Ts... params)
static void call(const ScriptRequest& UNUSED(rq), JS::MutableHandleValue UNUSED(rval), TC* c, F fptr, Ts... params)
{
(c->*fptr)(params...);
}
@@ -114,12 +114,12 @@ struct ScriptInterface_NativeMethodWrapper<void, TC>
bool ScriptInterface::call(JSContext* cx, uint argc, JS::Value* vp) \
{ \
JS::CallArgs args = JS::CallArgsFromVp(argc, vp); \
ScriptInterface::Request rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); \
ScriptRequest rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); \
BOOST_PP_REPEAT_##z (i, CONVERT_ARG, ~) \
JS::RootedValue rval(rq.cx); \
ScriptInterface_NativeWrapper<R>::template call<R( ScriptInterface::CmptPrivate* T0_TAIL_MAYBE_REF(z,i)) T0_TAIL(z,i)>(rq, &rval, fptr A0_TAIL(z,i)); \
args.rval().set(rval); \
return !ScriptInterface::IsExceptionPending(rq); \
return !ScriptException::IsPending(rq); \
}
BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
#undef OVERLOADS
@@ -130,14 +130,14 @@ BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
bool ScriptInterface::callMethod(JSContext* cx, uint argc, JS::Value* vp) \
{ \
JS::CallArgs args = JS::CallArgsFromVp(argc, vp); \
ScriptInterface::Request rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); \
ScriptRequest rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); \
TC* c = ScriptInterface::GetPrivate<TC>(rq, args, CLS); \
if (! c) return false; \
BOOST_PP_REPEAT_##z (i, CONVERT_ARG, ~) \
JS::RootedValue rval(rq.cx); \
ScriptInterface_NativeMethodWrapper<R, TC>::template call<R (TC::*)(T0_MAYBE_REF(z,i)) T0_TAIL(z,i)>(rq, &rval, c, fptr A0_TAIL(z,i)); \
args.rval().set(rval); \
return !ScriptInterface::IsExceptionPending(rq); \
return !ScriptException::IsPending(rq); \
}
BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
#undef OVERLOADS
@@ -148,27 +148,27 @@ BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
bool ScriptInterface::callMethodConst(JSContext* cx, uint argc, JS::Value* vp) \
{ \
JS::CallArgs args = JS::CallArgsFromVp(argc, vp); \
ScriptInterface::Request rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); \
ScriptRequest rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); \
TC* c = ScriptInterface::GetPrivate<TC>(rq, args, CLS); \
if (! c) return false; \
BOOST_PP_REPEAT_##z (i, CONVERT_ARG, ~) \
JS::RootedValue rval(rq.cx); \
ScriptInterface_NativeMethodWrapper<R, TC>::template call<R (TC::*)(T0_MAYBE_REF(z,i)) const T0_TAIL(z,i)>(rq, &rval, c, fptr A0_TAIL(z,i)); \
args.rval().set(rval); \
return !ScriptInterface::IsExceptionPending(rq); \
return !ScriptException::IsPending(rq); \
}
BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
#undef OVERLOADS
template<int i, typename T, typename... Ts>
static void AssignOrToJSValHelper(const ScriptInterface::Request& rq, JS::AutoValueVector& argv, const T& a, const Ts&... params)
static void AssignOrToJSValHelper(const ScriptRequest& rq, JS::AutoValueVector& argv, const T& a, const Ts&... params)
{
ScriptInterface::AssignOrToJSVal(rq, argv[i], a);
AssignOrToJSValHelper<i+1>(rq, argv, params...);
}
template<int i, typename... Ts>
static void AssignOrToJSValHelper(const ScriptInterface::Request& UNUSED(rq), JS::AutoValueVector& UNUSED(argv))
static void AssignOrToJSValHelper(const ScriptRequest& UNUSED(rq), JS::AutoValueVector& UNUSED(argv))
{
cassert(sizeof...(Ts) == 0);
// Nop, for terminating the template recursion.
@@ -177,7 +177,7 @@ static void AssignOrToJSValHelper(const ScriptInterface::Request& UNUSED(rq), JS
template<typename R, typename... Ts>
bool ScriptInterface::CallFunction(JS::HandleValue val, const char* name, R& ret, const Ts&... params) const
{
ScriptInterface::Request rq(this);
ScriptRequest rq(this);
JS::RootedValue jsRet(rq.cx);
JS::AutoValueVector argv(rq.cx);
argv.resize(sizeof...(Ts));
@@ -190,7 +190,7 @@ bool ScriptInterface::CallFunction(JS::HandleValue val, const char* name, R& ret
template<typename R, typename... Ts>
bool ScriptInterface::CallFunction(JS::HandleValue val, const char* name, JS::Rooted<R>* ret, const Ts&... params) const
{
ScriptInterface::Request rq(this);
ScriptRequest rq(this);
JS::MutableHandle<R> jsRet(ret);
JS::AutoValueVector argv(rq.cx);
argv.resize(sizeof...(Ts));
@@ -201,7 +201,7 @@ bool ScriptInterface::CallFunction(JS::HandleValue val, const char* name, JS::Ro
template<typename R, typename... Ts>
bool ScriptInterface::CallFunction(JS::HandleValue val, const char* name, JS::MutableHandle<R> ret, const Ts&... params) const
{
ScriptInterface::Request rq(this);
ScriptRequest rq(this);
JS::AutoValueVector argv(rq.cx);
argv.resize(sizeof...(Ts));
AssignOrToJSValHelper<0>(rq, argv, params...);
@@ -212,7 +212,7 @@ bool ScriptInterface::CallFunction(JS::HandleValue val, const char* name, JS::Mu
template<typename... Ts>
bool ScriptInterface::CallFunctionVoid(JS::HandleValue val, const char* name, const Ts&... params) const
{
ScriptInterface::Request rq(this);
ScriptRequest rq(this);
JS::RootedValue jsRet(rq.cx);
JS::AutoValueVector argv(rq.cx);
argv.resize(sizeof...(Ts));
+1 -47
View File
@@ -90,51 +90,6 @@ void GCSliceCallbackHook(JSRuntime* UNUSED(rt), JS::GCProgress progress, const J
#endif
}
namespace {
void ErrorReporter(JSContext* cx, const char* message, JSErrorReport* report)
{
ScriptInterface::Request rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface);
std::stringstream msg;
bool isWarning = JSREPORT_IS_WARNING(report->flags);
msg << (isWarning ? "JavaScript warning: " : "JavaScript error: ");
if (report->filename)
{
msg << report->filename;
msg << " line " << report->lineno << "\n";
}
msg << message;
// If there is an exception, then print its stack trace
JS::RootedValue excn(rq.cx);
if (JS_GetPendingException(rq.cx, &excn) && excn.isObject())
{
JS::RootedValue stackVal(rq.cx);
JS::RootedObject excnObj(rq.cx, &excn.toObject());
JS_GetProperty(rq.cx, excnObj, "stack", &stackVal);
std::string stackText;
ScriptInterface::FromJSVal(rq, stackVal, stackText);
std::istringstream stream(stackText);
for (std::string line; std::getline(stream, line);)
msg << "\n " << line;
}
if (isWarning)
LOGWARNING("%s", msg.str().c_str());
else
LOGERROR("%s", msg.str().c_str());
// When running under Valgrind, print more information in the error message
// VALGRIND_PRINTF_BACKTRACE("->");
}
} // anonymous namespace
shared_ptr<ScriptContext> ScriptContext::CreateContext(int contextSize, int heapGrowthBytesGCTrigger)
{
return shared_ptr<ScriptContext>(new ScriptContext(contextSize, heapGrowthBytesGCTrigger));
@@ -161,6 +116,7 @@ ScriptContext::ScriptContext(int contextSize, int heapGrowthBytesGCTrigger):
// We disable it to make it more clear if full GCs happen triggered by this JSAPI internal mechanism.
JS_SetGCParameter(m_rt, JSGC_DYNAMIC_HEAP_GROWTH, false);
JS_SetErrorReporter(m_rt, ScriptException::ErrorReporter);
m_cx = JS_NewContext(m_rt, STACK_CHUNK_SIZE);
ENSURE(m_cx); // TODO: error handling
@@ -172,8 +128,6 @@ ScriptContext::ScriptContext(int contextSize, int heapGrowthBytesGCTrigger):
JS_SetContextPrivate(m_cx, nullptr);
JS_SetErrorReporter(m_rt, ErrorReporter);
JS_SetGlobalJitCompilerOption(m_rt, JSJITCOMPILER_ION_ENABLE, 1);
JS_SetGlobalJitCompilerOption(m_rt, JSJITCOMPILER_BASELINE_ENABLE, 1);
+2 -1
View File
@@ -56,6 +56,7 @@ public:
int contextSize = DEFAULT_CONTEXT_SIZE,
int heapGrowthBytesGCTrigger = DEFAULT_HEAP_GROWTH_BYTES_GCTRIGGER);
/**
* MaybeIncrementalGC tries to determine whether a context-wide garbage collection would free up enough memory to
* be worth the amount of time it would take. It does this with our own logic and NOT some predefined JSAPI logic because
@@ -82,7 +83,7 @@ public:
* entering any compartment. It should only be used in specific situations, such as
* creating a new compartment, or as an unsafe alternative to GetJSRuntime.
* If you need the compartmented context of a ScriptInterface, you should create a
* ScriptInterface::Request and use the context from that.
* ScriptRequest and use the context from that.
*/
JSContext* GetGeneralJSContext() const { return m_cx; }
+34 -34
View File
@@ -24,7 +24,7 @@
#include "ps/utf16string.h"
#include "ps/CStr.h"
#define FAIL(msg) STMT(JS_ReportError(rq.cx, msg); return false)
#define FAIL(msg) STMT(LOGERROR(msg); return false)
// Implicit type conversions often hide bugs, so warn about them
#define WARN_IF_NOT(c, v) STMT(if (!(c)) { JS_ReportWarning(rq.cx, "Script value conversion check failed: %s (got type %s)", #c, InformalValueTypeName(v)); })
@@ -50,14 +50,14 @@ static const char* InformalValueTypeName(const JS::Value& v)
return "value";
}
template<> bool ScriptInterface::FromJSVal<bool>(const Request& rq, JS::HandleValue v, bool& out)
template<> bool ScriptInterface::FromJSVal<bool>(const ScriptRequest& rq, JS::HandleValue v, bool& out)
{
WARN_IF_NOT(v.isBoolean(), v);
out = JS::ToBoolean(v);
return true;
}
template<> bool ScriptInterface::FromJSVal<float>(const Request& rq, JS::HandleValue v, float& out)
template<> bool ScriptInterface::FromJSVal<float>(const ScriptRequest& rq, JS::HandleValue v, float& out)
{
double tmp;
WARN_IF_NOT(v.isNumber(), v);
@@ -67,7 +67,7 @@ template<> bool ScriptInterface::FromJSVal<float>(const Request& rq, JS::HandleV
return true;
}
template<> bool ScriptInterface::FromJSVal<double>(const Request& rq, JS::HandleValue v, double& out)
template<> bool ScriptInterface::FromJSVal<double>(const ScriptRequest& rq, JS::HandleValue v, double& out)
{
WARN_IF_NOT(v.isNumber(), v);
if (!JS::ToNumber(rq.cx, v, &out))
@@ -75,7 +75,7 @@ template<> bool ScriptInterface::FromJSVal<double>(const Request& rq, JS::Handl
return true;
}
template<> bool ScriptInterface::FromJSVal<i32>(const Request& rq, JS::HandleValue v, i32& out)
template<> bool ScriptInterface::FromJSVal<i32>(const ScriptRequest& rq, JS::HandleValue v, i32& out)
{
WARN_IF_NOT(v.isNumber(), v);
if (!JS::ToInt32(rq.cx, v, &out))
@@ -83,7 +83,7 @@ template<> bool ScriptInterface::FromJSVal<i32>(const Request& rq, JS::HandleVa
return true;
}
template<> bool ScriptInterface::FromJSVal<u32>(const Request& rq, JS::HandleValue v, u32& out)
template<> bool ScriptInterface::FromJSVal<u32>(const ScriptRequest& rq, JS::HandleValue v, u32& out)
{
WARN_IF_NOT(v.isNumber(), v);
if (!JS::ToUint32(rq.cx, v, &out))
@@ -91,7 +91,7 @@ template<> bool ScriptInterface::FromJSVal<u32>(const Request& rq, JS::HandleVa
return true;
}
template<> bool ScriptInterface::FromJSVal<u16>(const Request& rq, JS::HandleValue v, u16& out)
template<> bool ScriptInterface::FromJSVal<u16>(const ScriptRequest& rq, JS::HandleValue v, u16& out)
{
WARN_IF_NOT(v.isNumber(), v);
if (!JS::ToUint16(rq.cx, v, &out))
@@ -99,7 +99,7 @@ template<> bool ScriptInterface::FromJSVal<u16>(const Request& rq, JS::HandleVa
return true;
}
template<> bool ScriptInterface::FromJSVal<u8>(const Request& rq, JS::HandleValue v, u8& out)
template<> bool ScriptInterface::FromJSVal<u8>(const ScriptRequest& rq, JS::HandleValue v, u8& out)
{
u16 tmp;
WARN_IF_NOT(v.isNumber(), v);
@@ -109,7 +109,7 @@ template<> bool ScriptInterface::FromJSVal<u8>(const Request& rq, JS::HandleVal
return true;
}
template<> bool ScriptInterface::FromJSVal<std::wstring>(const Request& rq, JS::HandleValue v, std::wstring& out)
template<> bool ScriptInterface::FromJSVal<std::wstring>(const ScriptRequest& rq, JS::HandleValue v, std::wstring& out)
{
WARN_IF_NOT(v.isString() || v.isNumber(), v); // allow implicit number conversions
JS::RootedString str(rq.cx, JS::ToString(rq.cx, v));
@@ -139,7 +139,7 @@ template<> bool ScriptInterface::FromJSVal<std::wstring>(const Request& rq, JS:
return true;
}
template<> bool ScriptInterface::FromJSVal<Path>(const Request& rq, JS::HandleValue v, Path& out)
template<> bool ScriptInterface::FromJSVal<Path>(const ScriptRequest& rq, JS::HandleValue v, Path& out)
{
std::wstring string;
if (!FromJSVal(rq, v, string))
@@ -148,7 +148,7 @@ template<> bool ScriptInterface::FromJSVal<Path>(const Request& rq, JS::HandleV
return true;
}
template<> bool ScriptInterface::FromJSVal<std::string>(const Request& rq, JS::HandleValue v, std::string& out)
template<> bool ScriptInterface::FromJSVal<std::string>(const ScriptRequest& rq, JS::HandleValue v, std::string& out)
{
std::wstring wideout;
if (!FromJSVal(rq, v, wideout))
@@ -157,17 +157,17 @@ template<> bool ScriptInterface::FromJSVal<std::string>(const Request& rq, JS::
return true;
}
template<> bool ScriptInterface::FromJSVal<CStr8>(const Request& rq, JS::HandleValue v, CStr8& out)
template<> bool ScriptInterface::FromJSVal<CStr8>(const ScriptRequest& rq, JS::HandleValue v, CStr8& out)
{
return ScriptInterface::FromJSVal(rq, v, static_cast<std::string&>(out));
}
template<> bool ScriptInterface::FromJSVal<CStrW>(const Request& rq, JS::HandleValue v, CStrW& out)
template<> bool ScriptInterface::FromJSVal<CStrW>(const ScriptRequest& rq, JS::HandleValue v, CStrW& out)
{
return ScriptInterface::FromJSVal(rq, v, static_cast<std::wstring&>(out));
}
template<> bool ScriptInterface::FromJSVal<Entity>(const Request& rq, JS::HandleValue v, Entity& out)
template<> bool ScriptInterface::FromJSVal<Entity>(const ScriptRequest& rq, JS::HandleValue v, Entity& out)
{
if (!v.isObject())
FAIL("Argument must be an object");
@@ -197,42 +197,42 @@ template<> bool ScriptInterface::FromJSVal<Entity>(const Request& rq, JS::Handl
////////////////////////////////////////////////////////////////
// Primitive types:
template<> void ScriptInterface::ToJSVal<bool>(const Request& UNUSED(rq), JS::MutableHandleValue ret, const bool& val)
template<> void ScriptInterface::ToJSVal<bool>(const ScriptRequest& UNUSED(rq), JS::MutableHandleValue ret, const bool& val)
{
ret.setBoolean(val);
}
template<> void ScriptInterface::ToJSVal<float>(const Request& UNUSED(rq), JS::MutableHandleValue ret, const float& val)
template<> void ScriptInterface::ToJSVal<float>(const ScriptRequest& UNUSED(rq), JS::MutableHandleValue ret, const float& val)
{
ret.set(JS::NumberValue(val));
}
template<> void ScriptInterface::ToJSVal<double>(const Request& UNUSED(rq), JS::MutableHandleValue ret, const double& val)
template<> void ScriptInterface::ToJSVal<double>(const ScriptRequest& UNUSED(rq), JS::MutableHandleValue ret, const double& val)
{
ret.set(JS::NumberValue(val));
}
template<> void ScriptInterface::ToJSVal<i32>(const Request& UNUSED(rq), JS::MutableHandleValue ret, const i32& val)
template<> void ScriptInterface::ToJSVal<i32>(const ScriptRequest& UNUSED(rq), JS::MutableHandleValue ret, const i32& val)
{
ret.set(JS::NumberValue(val));
}
template<> void ScriptInterface::ToJSVal<u16>(const Request& UNUSED(rq), JS::MutableHandleValue ret, const u16& val)
template<> void ScriptInterface::ToJSVal<u16>(const ScriptRequest& UNUSED(rq), JS::MutableHandleValue ret, const u16& val)
{
ret.set(JS::NumberValue(val));
}
template<> void ScriptInterface::ToJSVal<u8>(const Request& UNUSED(rq), JS::MutableHandleValue ret, const u8& val)
template<> void ScriptInterface::ToJSVal<u8>(const ScriptRequest& UNUSED(rq), JS::MutableHandleValue ret, const u8& val)
{
ret.set(JS::NumberValue(val));
}
template<> void ScriptInterface::ToJSVal<u32>(const Request& UNUSED(rq), JS::MutableHandleValue ret, const u32& val)
template<> void ScriptInterface::ToJSVal<u32>(const ScriptRequest& UNUSED(rq), JS::MutableHandleValue ret, const u32& val)
{
ret.set(JS::NumberValue(val));
}
template<> void ScriptInterface::ToJSVal<std::wstring>(const Request& rq, JS::MutableHandleValue ret, const std::wstring& val)
template<> void ScriptInterface::ToJSVal<std::wstring>(const ScriptRequest& rq, JS::MutableHandleValue ret, const std::wstring& val)
{
utf16string utf16(val.begin(), val.end());
JS::RootedString str(rq.cx, JS_NewUCStringCopyN(rq.cx, reinterpret_cast<const char16_t*> (utf16.c_str()), utf16.length()));
@@ -242,22 +242,22 @@ template<> void ScriptInterface::ToJSVal<std::wstring>(const Request& rq, JS::M
ret.setUndefined();
}
template<> void ScriptInterface::ToJSVal<Path>(const Request& rq, JS::MutableHandleValue ret, const Path& val)
template<> void ScriptInterface::ToJSVal<Path>(const ScriptRequest& rq, JS::MutableHandleValue ret, const Path& val)
{
ToJSVal(rq, ret, val.string());
}
template<> void ScriptInterface::ToJSVal<std::string>(const Request& rq, JS::MutableHandleValue ret, const std::string& val)
template<> void ScriptInterface::ToJSVal<std::string>(const ScriptRequest& rq, JS::MutableHandleValue ret, const std::string& val)
{
ToJSVal(rq, ret, static_cast<const std::wstring>(CStr(val).FromUTF8()));
}
template<> void ScriptInterface::ToJSVal<const wchar_t*>(const Request& rq, JS::MutableHandleValue ret, const wchar_t* const& val)
template<> void ScriptInterface::ToJSVal<const wchar_t*>(const ScriptRequest& rq, JS::MutableHandleValue ret, const wchar_t* const& val)
{
ToJSVal(rq, ret, std::wstring(val));
}
template<> void ScriptInterface::ToJSVal<const char*>(const Request& rq, JS::MutableHandleValue ret, const char* const& val)
template<> void ScriptInterface::ToJSVal<const char*>(const ScriptRequest& rq, JS::MutableHandleValue ret, const char* const& val)
{
JS::RootedString str(rq.cx, JS_NewStringCopyZ(rq.cx, val));
if (str)
@@ -267,11 +267,11 @@ template<> void ScriptInterface::ToJSVal<const char*>(const Request& rq, JS::Mu
}
#define TOJSVAL_CHAR(N) \
template<> void ScriptInterface::ToJSVal<wchar_t[N]>(const Request& rq, JS::MutableHandleValue ret, const wchar_t (&val)[N]) \
template<> void ScriptInterface::ToJSVal<wchar_t[N]>(const ScriptRequest& rq, JS::MutableHandleValue ret, const wchar_t (&val)[N]) \
{ \
ToJSVal(rq, ret, static_cast<const wchar_t*>(val)); \
} \
template<> void ScriptInterface::ToJSVal<char[N]>(const Request& rq, JS::MutableHandleValue ret, const char (&val)[N]) \
template<> void ScriptInterface::ToJSVal<char[N]>(const ScriptRequest& rq, JS::MutableHandleValue ret, const char (&val)[N]) \
{ \
ToJSVal(rq, ret, static_cast<const char*>(val)); \
}
@@ -300,12 +300,12 @@ TOJSVAL_CHAR(35)
TOJSVAL_CHAR(256)
#undef TOJSVAL_CHAR
template<> void ScriptInterface::ToJSVal<CStrW>(const Request& rq, JS::MutableHandleValue ret, const CStrW& val)
template<> void ScriptInterface::ToJSVal<CStrW>(const ScriptRequest& rq, JS::MutableHandleValue ret, const CStrW& val)
{
ToJSVal(rq, ret, static_cast<const std::wstring&>(val));
}
template<> void ScriptInterface::ToJSVal<CStr8>(const Request& rq, JS::MutableHandleValue ret, const CStr8& val)
template<> void ScriptInterface::ToJSVal<CStr8>(const ScriptRequest& rq, JS::MutableHandleValue ret, const CStr8& val)
{
ToJSVal(rq, ret, static_cast<const std::string&>(val));
}
@@ -326,23 +326,23 @@ JSVAL_VECTOR(std::vector<std::string>)
class IComponent;
template<> void ScriptInterface::ToJSVal<std::vector<IComponent*> >(const Request& rq, JS::MutableHandleValue ret, const std::vector<IComponent*>& val)
template<> void ScriptInterface::ToJSVal<std::vector<IComponent*> >(const ScriptRequest& rq, JS::MutableHandleValue ret, const std::vector<IComponent*>& val)
{
ToJSVal_vector(rq, ret, val);
}
template<> bool ScriptInterface::FromJSVal<std::vector<Entity> >(const Request& rq, JS::HandleValue v, std::vector<Entity>& out)
template<> bool ScriptInterface::FromJSVal<std::vector<Entity> >(const ScriptRequest& rq, JS::HandleValue v, std::vector<Entity>& out)
{
return FromJSVal_vector(rq, v, out);
}
template<> void ScriptInterface::ToJSVal<CVector2D>(const Request& rq, JS::MutableHandleValue ret, const CVector2D& val)
template<> void ScriptInterface::ToJSVal<CVector2D>(const ScriptRequest& rq, JS::MutableHandleValue ret, const CVector2D& val)
{
std::vector<float> vec = {val.X, val.Y};
ToJSVal_vector(rq, ret, vec);
}
template<> bool ScriptInterface::FromJSVal<CVector2D>(const Request& rq, JS::HandleValue v, CVector2D& out)
template<> bool ScriptInterface::FromJSVal<CVector2D>(const ScriptRequest& rq, JS::HandleValue v, CVector2D& out)
{
std::vector<float> vec;
+6 -6
View File
@@ -23,7 +23,7 @@
#include <limits>
template<typename T> static void ToJSVal_vector(const ScriptInterface::Request& rq, JS::MutableHandleValue ret, const std::vector<T>& val)
template<typename T> static void ToJSVal_vector(const ScriptRequest& rq, JS::MutableHandleValue ret, const std::vector<T>& val)
{
JS::RootedObject obj(rq.cx, JS_NewArrayObject(rq.cx, 0));
if (!obj)
@@ -42,9 +42,9 @@ template<typename T> static void ToJSVal_vector(const ScriptInterface::Request&
ret.setObject(*obj);
}
#define FAIL(msg) STMT(JS_ReportError(rq.cx, msg); return false)
#define FAIL(msg) STMT(ScriptException::Raise(rq, msg); return false)
template<typename T> static bool FromJSVal_vector(const ScriptInterface::Request& rq, JS::HandleValue v, std::vector<T>& out)
template<typename T> static bool FromJSVal_vector(const ScriptRequest& rq, JS::HandleValue v, std::vector<T>& out)
{
JS::RootedObject obj(rq.cx);
if (!v.isObject())
@@ -76,16 +76,16 @@ template<typename T> static bool FromJSVal_vector(const ScriptInterface::Request
#undef FAIL
#define JSVAL_VECTOR(T) \
template<> void ScriptInterface::ToJSVal<std::vector<T> >(const ScriptInterface::Request& rq, JS::MutableHandleValue ret, const std::vector<T>& val) \
template<> void ScriptInterface::ToJSVal<std::vector<T> >(const ScriptRequest& rq, JS::MutableHandleValue ret, const std::vector<T>& val) \
{ \
ToJSVal_vector(rq, ret, val); \
} \
template<> bool ScriptInterface::FromJSVal<std::vector<T> >(const ScriptInterface::Request& rq, JS::HandleValue v, std::vector<T>& out) \
template<> bool ScriptInterface::FromJSVal<std::vector<T> >(const ScriptRequest& rq, JS::HandleValue v, std::vector<T>& out) \
{ \
return FromJSVal_vector(rq, v, out); \
}
template<typename T> bool ScriptInterface::FromJSProperty(const ScriptInterface::Request& rq, const JS::HandleValue val, const char* name, T& ret, bool strict)
template<typename T> bool ScriptInterface::FromJSProperty(const ScriptRequest& rq, const JS::HandleValue val, const char* name, T& ret, bool strict)
{
if (!val.isObject())
return false;
+151
View File
@@ -0,0 +1,151 @@
/* Copyright (C) 2020 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 <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "ScriptExceptions.h"
#include "ps/CLogger.h"
#include "scriptinterface/ScriptInterface.h"
bool ScriptException::IsPending(const ScriptRequest& rq)
{
return JS_IsExceptionPending(rq.cx);
}
bool ScriptException::CatchPending(const ScriptRequest& rq)
{
if (!JS_IsExceptionPending(rq.cx))
return false;
JS::RootedValue excn(rq.cx);
ENSURE(JS_GetPendingException(rq.cx, &excn));
if (excn.isUndefined())
{
LOGERROR("JavaScript error: (undefined)");
JS_ClearPendingException(rq.cx);
return true;
}
// As of SM45/52, there is no way to recover a stack in case the thrown thing is not an Error object.
if (!excn.isObject())
{
CStr error;
ScriptInterface::FromJSVal(rq, excn, error);
LOGERROR("JavaScript error: %s", error);
JS_ClearPendingException(rq.cx);
return true;
}
JS::RootedObject excnObj(rq.cx, &excn.toObject());
JSErrorReport* report = JS_ErrorFromException(rq.cx, excnObj);
if (!report)
{
CStr error;
ScriptInterface::FromJSVal(rq, excn, error);
LOGERROR("JavaScript error: %s", error);
JS_ClearPendingException(rq.cx);
return true;
}
std::stringstream msg;
msg << (JSREPORT_IS_EXCEPTION(report->flags) ? "JavaScript exception: " : "JavaScript error: ");
if (report->filename)
{
msg << report->filename;
msg << " line " << report->lineno << "\n";
}
// TODO SM52:
// msg << report->message();
JS::RootedObject stackObj(rq.cx, ExceptionStackOrNull(rq.cx, excnObj));
JS::RootedValue stackVal(rq.cx, JS::ObjectOrNullValue(stackObj));
if (!stackVal.isNull())
{
std::string stackText;
ScriptInterface::FromJSVal(rq, stackVal, stackText);
std::istringstream stream(stackText);
for (std::string line; std::getline(stream, line);)
msg << "\n " << line;
}
LOGERROR("%s", msg.str().c_str());
// When running under Valgrind, print more information in the error message
// VALGRIND_PRINTF_BACKTRACE("->");
JS_ClearPendingException(rq.cx);
return true;
}
void ScriptException::ErrorReporter(JSContext* cx, const char* message, JSErrorReport* report)
{
if (!ScriptInterface::GetScriptInterfaceAndCBData(cx))
{
LOGERROR("Javascript - Out of Memory error");
return;
}
ScriptRequest rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface);
std::stringstream msg;
bool isWarning = JSREPORT_IS_WARNING(report->flags);
msg << (isWarning ? "JavaScript warning: " : "JavaScript error: ");
if (report->filename)
{
msg << report->filename;
msg << " line " << report->lineno << "\n";
}
msg << message;
// If there is an exception, then print its stack trace
JS::RootedValue excn(rq.cx);
if (JS_GetPendingException(rq.cx, &excn) && excn.isObject())
{
JS::RootedValue stackVal(rq.cx);
JS::RootedObject excnObj(rq.cx, &excn.toObject());
JS_GetProperty(rq.cx, excnObj, "stack", &stackVal);
std::string stackText;
ScriptInterface::FromJSVal(rq, stackVal, stackText);
std::istringstream stream(stackText);
for (std::string line; std::getline(stream, line);)
msg << "\n " << line;
}
if (isWarning)
LOGWARNING("%s", msg.str().c_str());
else
LOGERROR("%s", msg.str().c_str());
// When running under Valgrind, print more information in the error message
// VALGRIND_PRINTF_BACKTRACE("->");
}
void ScriptException::Raise(const ScriptRequest& rq, const char* format, ...)
{
va_list ap;
va_start(ap, format);
JS_ReportError(rq.cx, format, ap);
va_end(ap);
}
+53
View File
@@ -0,0 +1,53 @@
/* Copyright (C) 2020 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 <http://www.gnu.org/licenses/>.
*/
#ifndef INCLUDED_SCRIPTEXCEPTIONS
#define INCLUDED_SCRIPTEXCEPTIONS
struct JSContext;
class JSErrorReport;
class ScriptRequest;
namespace ScriptException
{
/**
* @return Whether there is a JS exception pending.
*/
bool IsPending(const ScriptRequest& rq);
/**
* Log and then clear the current pending exception. This function should always be called after calling a
* JS script (or anything that can throw JS errors, such as structured clones),
* in case that script doesn't catch an exception thrown during its execution.
* If no exception is pending, this does nothing.
* Note that JS code that wants to throw errors should throw new Error(...), otherwise the stack cannot be used.
* @return Whether there was a pending exception.
*/
bool CatchPending(const ScriptRequest& rq);
void ErrorReporter(JSContext* rt, const char* message, JSErrorReport* report);
/**
* Raise a JS exception from C++ code.
* This is only really relevant in JSNative functions that don't use ObjectOpResult,
* as the latter overwrites the pending exception.
* Prefer either simply logging an error if you know a stack-trace will be raised elsewhere.
*/
void Raise(const ScriptRequest& rq, const char* format, ...);
}
#endif // INCLUDED_SCRIPTEXCEPTIONS
+94 -114
View File
@@ -62,7 +62,7 @@ struct ScriptInterface_impl
// members have to be called before the context destructor.
shared_ptr<ScriptContext> m_context;
friend ScriptInterface::Request;
friend ScriptRequest;
private:
JSContext* m_cx;
JS::PersistentRootedObject m_glob; // global scope object
@@ -72,7 +72,7 @@ struct ScriptInterface_impl
JS::PersistentRootedObject m_nativeScope; // native function scope object
};
ScriptInterface::Request::Request(const ScriptInterface& scriptInterface) :
ScriptRequest::ScriptRequest(const ScriptInterface& scriptInterface) :
cx(scriptInterface.m->m_cx)
{
JS_BeginRequest(cx);
@@ -80,12 +80,12 @@ ScriptInterface::Request::Request(const ScriptInterface& scriptInterface) :
glob = JS::CurrentGlobalOrNull(cx);
}
JS::Value ScriptInterface::Request::globalValue() const
JS::Value ScriptRequest::globalValue() const
{
return JS::ObjectValue(*glob);
}
ScriptInterface::Request::~Request()
ScriptRequest::~ScriptRequest()
{
JS_LeaveCompartment(cx, m_formerCompartment);
JS_EndRequest(cx);
@@ -108,7 +108,7 @@ JSClass global_class = {
bool print(JSContext* cx, uint argc, JS::Value* vp)
{
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
ScriptInterface::Request rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); \
ScriptRequest rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); \
for (uint i = 0; i < args.length(); ++i)
{
@@ -131,7 +131,7 @@ bool logmsg(JSContext* cx, uint argc, JS::Value* vp)
return true;
}
ScriptInterface::Request rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); \
ScriptRequest rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); \
std::wstring str;
if (!ScriptInterface::FromJSVal(rq, args[0], str))
return false;
@@ -149,7 +149,7 @@ bool warn(JSContext* cx, uint argc, JS::Value* vp)
return true;
}
ScriptInterface::Request rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); \
ScriptRequest rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); \
std::wstring str;
if (!ScriptInterface::FromJSVal(rq, args[0], str))
return false;
@@ -167,7 +167,7 @@ bool error(JSContext* cx, uint argc, JS::Value* vp)
return true;
}
ScriptInterface::Request rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); \
ScriptRequest rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); \
std::wstring str;
if (!ScriptInterface::FromJSVal(rq, args[0], str))
return false;
@@ -185,7 +185,7 @@ bool deepcopy(JSContext* cx, uint argc, JS::Value* vp)
return true;
}
ScriptInterface::Request rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); \
ScriptRequest rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); \
JS::RootedValue ret(cx);
if (!JS_StructuredClone(rq.cx, args[0], &ret, NULL, NULL))
return false;
@@ -197,11 +197,11 @@ bool deepcopy(JSContext* cx, uint argc, JS::Value* vp)
bool deepfreeze(JSContext* cx, uint argc, JS::Value* vp)
{
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
ScriptInterface::Request rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); \
ScriptRequest rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); \
if (args.length() != 1 || !args.get(0).isObject())
{
JS_ReportError(cx, "deepfreeze requires exactly one object as an argument.");
ScriptException::Raise(rq, "deepfreeze requires exactly one object as an argument.");
return false;
}
@@ -215,7 +215,7 @@ bool ProfileStart(JSContext* cx, uint argc, JS::Value* vp)
const char* name = "(ProfileStart)";
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
ScriptInterface::Request rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); \
ScriptRequest rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); \
if (args.length() >= 1)
{
std::string str;
@@ -257,7 +257,7 @@ bool ProfileAttribute(JSContext* cx, uint argc, JS::Value* vp)
const char* name = "(ProfileAttribute)";
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
ScriptInterface::Request rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); \
ScriptRequest rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); \
if (args.length() >= 1)
{
std::string str;
@@ -374,7 +374,7 @@ ScriptInterface::ScriptInterface(const char* nativeScopeName, const char* debugN
g_ScriptStatsTable->Add(this, debugName);
}
Request rq(this);
ScriptRequest rq(this);
m_CmptPrivate.pScriptInterface = this;
JS_SetCompartmentPrivate(js::GetObjectCompartment(rq.glob), (void*)&m_CmptPrivate);
}
@@ -421,7 +421,7 @@ bool ScriptInterface::LoadGlobalScripts()
bool ScriptInterface::ReplaceNondeterministicRNG(boost::rand48& rng)
{
Request rq(this);
ScriptRequest rq(this);
JS::RootedValue math(rq.cx);
JS::RootedObject global(rq.cx, rq.glob);
if (JS_GetProperty(rq.cx, global, "Math", &math) && math.isObject())
@@ -436,6 +436,7 @@ bool ScriptInterface::ReplaceNondeterministicRNG(boost::rand48& rng)
}
}
ScriptException::CatchPending(rq);
LOGERROR("ReplaceNondeterministicRNG: failed to replace Math.random");
return false;
}
@@ -457,7 +458,7 @@ shared_ptr<ScriptContext> ScriptInterface::GetContext() const
void ScriptInterface::CallConstructor(JS::HandleValue ctor, JS::HandleValueArray argv, JS::MutableHandleValue out) const
{
Request rq(this);
ScriptRequest rq(this);
if (!ctor.isObject())
{
LOGERROR("CallConstructor: ctor is not an object");
@@ -471,7 +472,7 @@ void ScriptInterface::CallConstructor(JS::HandleValue ctor, JS::HandleValueArray
void ScriptInterface::DefineCustomObjectType(JSClass *clasp, JSNative constructor, uint minArgs, JSPropertySpec *ps, JSFunctionSpec *fs, JSPropertySpec *static_ps, JSFunctionSpec *static_fs)
{
Request rq(this);
ScriptRequest rq(this);
std::string typeName = clasp->name;
if (m_CustomObjectTypes.find(typeName) != m_CustomObjectTypes.end())
@@ -488,7 +489,10 @@ void ScriptInterface::DefineCustomObjectType(JSClass *clasp, JSNative constructo
static_ps, static_fs)); // Constructor properties, methods
if (obj == NULL)
{
ScriptException::CatchPending(rq);
throw PSERROR_Scripting_DefineType_CreationFailed();
}
CustomType& type = m_CustomObjectTypes[typeName];
@@ -504,14 +508,14 @@ JSObject* ScriptInterface::CreateCustomObject(const std::string& typeName) const
if (it == m_CustomObjectTypes.end())
throw PSERROR_Scripting_TypeDoesNotExist();
Request rq(this);
ScriptRequest rq(this);
JS::RootedObject prototype(rq.cx, it->second.m_Prototype.get());
return JS_NewObjectWithGivenProto(rq.cx, it->second.m_Class, prototype);
}
bool ScriptInterface::CallFunction_(JS::HandleValue val, const char* name, JS::HandleValueArray argv, JS::MutableHandleValue ret) const
{
Request rq(this);
ScriptRequest rq(this);
JS::RootedObject obj(rq.cx);
if (!JS_ValueToObject(rq.cx, val, &obj) || !obj)
return false;
@@ -522,10 +526,14 @@ bool ScriptInterface::CallFunction_(JS::HandleValue val, const char* name, JS::H
if (!JS_HasProperty(rq.cx, obj, name, &found) || !found)
return false;
return JS_CallFunctionName(rq.cx, obj, name, argv, ret);
if (JS_CallFunctionName(rq.cx, obj, name, argv, ret))
return true;
ScriptException::CatchPending(rq);
return false;
}
bool ScriptInterface::CreateObject_(const Request& rq, JS::MutableHandleObject object)
bool ScriptInterface::CreateObject_(const ScriptRequest& rq, JS::MutableHandleObject object)
{
object.set(JS_NewPlainObject(rq.cx));
@@ -535,7 +543,7 @@ bool ScriptInterface::CreateObject_(const Request& rq, JS::MutableHandleObject o
return true;
}
void ScriptInterface::CreateArray(const Request& rq, JS::MutableHandleValue objectValue, size_t length)
void ScriptInterface::CreateArray(const ScriptRequest& rq, JS::MutableHandleValue objectValue, size_t length)
{
objectValue.setObjectOrNull(JS_NewArrayObject(rq.cx, length));
if (!objectValue.isObject())
@@ -544,7 +552,7 @@ void ScriptInterface::CreateArray(const Request& rq, JS::MutableHandleValue obje
bool ScriptInterface::SetGlobal_(const char* name, JS::HandleValue value, bool replace, bool constant, bool enumerate)
{
Request rq(this);
ScriptRequest rq(this);
JS::RootedObject global(rq.cx, rq.glob);
bool found;
@@ -560,7 +568,7 @@ bool ScriptInterface::SetGlobal_(const char* name, JS::HandleValue value, bool r
{
if (!replace)
{
JS_ReportError(rq.cx, "SetGlobal \"%s\" called multiple times", name);
ScriptException::Raise(rq, "SetGlobal \"%s\" called multiple times", name);
return false;
}
@@ -568,7 +576,7 @@ bool ScriptInterface::SetGlobal_(const char* name, JS::HandleValue value, bool r
// instead of using SetGlobal.
if (!desc.configurable())
{
JS_ReportError(rq.cx, "The global \"%s\" is permanent and cannot be hotloaded", name);
ScriptException::Raise(rq, "The global \"%s\" is permanent and cannot be hotloaded", name);
return false;
}
@@ -588,7 +596,7 @@ bool ScriptInterface::SetGlobal_(const char* name, JS::HandleValue value, bool r
bool ScriptInterface::SetProperty_(JS::HandleValue obj, const char* name, JS::HandleValue value, bool constant, bool enumerate) const
{
Request rq(this);
ScriptRequest rq(this);
uint attrs = 0;
if (constant)
attrs |= JSPROP_READONLY | JSPROP_PERMANENT;
@@ -599,14 +607,12 @@ bool ScriptInterface::SetProperty_(JS::HandleValue obj, const char* name, JS::Ha
return false;
JS::RootedObject object(rq.cx, &obj.toObject());
if (!JS_DefineProperty(rq.cx, object, name, value, attrs))
return false;
return true;
return JS_DefineProperty(rq.cx, object, name, value, attrs);
}
bool ScriptInterface::SetProperty_(JS::HandleValue obj, const wchar_t* name, JS::HandleValue value, bool constant, bool enumerate) const
{
Request rq(this);
ScriptRequest rq(this);
uint attrs = 0;
if (constant)
attrs |= JSPROP_READONLY | JSPROP_PERMANENT;
@@ -618,14 +624,12 @@ bool ScriptInterface::SetProperty_(JS::HandleValue obj, const wchar_t* name, JS:
JS::RootedObject object(rq.cx, &obj.toObject());
utf16string name16(name, name + wcslen(name));
if (!JS_DefineUCProperty(rq.cx, object, reinterpret_cast<const char16_t*>(name16.c_str()), name16.length(), value, attrs))
return false;
return true;
return JS_DefineUCProperty(rq.cx, object, reinterpret_cast<const char16_t*>(name16.c_str()), name16.length(), value, attrs);
}
bool ScriptInterface::SetPropertyInt_(JS::HandleValue obj, int name, JS::HandleValue value, bool constant, bool enumerate) const
{
Request rq(this);
ScriptRequest rq(this);
uint attrs = 0;
if (constant)
attrs |= JSPROP_READONLY | JSPROP_PERMANENT;
@@ -637,9 +641,7 @@ bool ScriptInterface::SetPropertyInt_(JS::HandleValue obj, int name, JS::HandleV
JS::RootedObject object(rq.cx, &obj.toObject());
JS::RootedId id(rq.cx, INT_TO_JSID(name));
if (!JS_DefinePropertyById(rq.cx, object, id, value, attrs))
return false;
return true;
return JS_DefinePropertyById(rq.cx, object, id, value, attrs);
}
bool ScriptInterface::GetProperty(JS::HandleValue obj, const char* name, JS::MutableHandleValue out) const
@@ -649,7 +651,7 @@ bool ScriptInterface::GetProperty(JS::HandleValue obj, const char* name, JS::Mut
bool ScriptInterface::GetProperty(JS::HandleValue obj, const char* name, JS::MutableHandleObject out) const
{
Request rq(this);
ScriptRequest rq(this);
JS::RootedValue val(rq.cx);
if (!GetProperty_(obj, name, &val))
return false;
@@ -670,33 +672,28 @@ bool ScriptInterface::GetPropertyInt(JS::HandleValue obj, int name, JS::MutableH
bool ScriptInterface::GetProperty_(JS::HandleValue obj, const char* name, JS::MutableHandleValue out) const
{
Request rq(this);
ScriptRequest rq(this);
if (!obj.isObject())
return false;
JS::RootedObject object(rq.cx, &obj.toObject());
if (!JS_GetProperty(rq.cx, object, name, out))
return false;
return true;
return JS_GetProperty(rq.cx, object, name, out);
}
bool ScriptInterface::GetPropertyInt_(JS::HandleValue obj, int name, JS::MutableHandleValue out) const
{
Request rq(this);
ScriptRequest rq(this);
JS::RootedId nameId(rq.cx, INT_TO_JSID(name));
if (!obj.isObject())
return false;
JS::RootedObject object(rq.cx, &obj.toObject());
if (!JS_GetPropertyById(rq.cx, object, nameId, out))
return false;
return true;
return JS_GetPropertyById(rq.cx, object, nameId, out);
}
bool ScriptInterface::HasProperty(JS::HandleValue obj, const char* name) const
{
// TODO: proper errorhandling
Request rq(this);
ScriptRequest rq(this);
if (!obj.isObject())
return false;
JS::RootedObject object(rq.cx, &obj.toObject());
@@ -709,7 +706,7 @@ bool ScriptInterface::HasProperty(JS::HandleValue obj, const char* name) const
bool ScriptInterface::EnumeratePropertyNames(JS::HandleValue objVal, bool enumerableOnly, std::vector<std::string>& out) const
{
Request rq(this);
ScriptRequest rq(this);
if (!objVal.isObjectOrNull())
{
@@ -748,7 +745,7 @@ bool ScriptInterface::EnumeratePropertyNames(JS::HandleValue objVal, bool enumer
bool ScriptInterface::SetPrototype(JS::HandleValue objVal, JS::HandleValue protoVal)
{
Request rq(this);
ScriptRequest rq(this);
if (!objVal.isObject() || !protoVal.isObject())
return false;
JS::RootedObject obj(rq.cx, &objVal.toObject());
@@ -758,7 +755,7 @@ bool ScriptInterface::SetPrototype(JS::HandleValue objVal, JS::HandleValue proto
bool ScriptInterface::FreezeObject(JS::HandleValue objVal, bool deep) const
{
Request rq(this);
ScriptRequest rq(this);
if (!objVal.isObject())
return false;
@@ -772,7 +769,7 @@ bool ScriptInterface::FreezeObject(JS::HandleValue objVal, bool deep) const
bool ScriptInterface::LoadScript(const VfsPath& filename, const std::string& code) const
{
Request rq(this);
ScriptRequest rq(this);
JS::RootedObject global(rq.cx, rq.glob);
utf16string codeUtf16(code.begin(), code.end());
uint lineNo = 1;
@@ -788,15 +785,22 @@ bool ScriptInterface::LoadScript(const VfsPath& filename, const std::string& cod
JS::AutoObjectVector emptyScopeChain(rq.cx);
if (!JS::CompileFunction(rq.cx, emptyScopeChain, options, NULL, 0, NULL,
reinterpret_cast<const char16_t*>(codeUtf16.c_str()), (uint)(codeUtf16.length()), &func))
{
ScriptException::CatchPending(rq);
return false;
}
JS::RootedValue rval(rq.cx);
return JS_CallFunction(rq.cx, nullptr, func, JS::HandleValueArray::empty(), &rval);
if (JS_CallFunction(rq.cx, nullptr, func, JS::HandleValueArray::empty(), &rval))
return true;
ScriptException::CatchPending(rq);
return false;
}
bool ScriptInterface::LoadGlobalScript(const VfsPath& filename, const std::wstring& code) const
{
Request rq(this);
ScriptRequest rq(this);
utf16string codeUtf16(code.begin(), code.end());
uint lineNo = 1;
// CompileOptions does not copy the contents of the filename string pointer.
@@ -806,13 +810,16 @@ bool ScriptInterface::LoadGlobalScript(const VfsPath& filename, const std::wstri
JS::RootedValue rval(rq.cx);
JS::CompileOptions opts(rq.cx);
opts.setFileAndLine(filenameStr.c_str(), lineNo);
return JS::Evaluate(rq.cx, opts,
reinterpret_cast<const char16_t*>(codeUtf16.c_str()), (uint)(codeUtf16.length()), &rval);
if (JS::Evaluate(rq.cx, opts, reinterpret_cast<const char16_t*>(codeUtf16.c_str()), (uint)(codeUtf16.length()), &rval))
return true;
ScriptException::CatchPending(rq);
return false;
}
bool ScriptInterface::LoadGlobalScriptFile(const VfsPath& path) const
{
Request rq(this);
ScriptRequest rq(this);
if (!VfsFileExists(path))
{
LOGERROR("File '%s' does not exist", path.string8());
@@ -840,66 +847,57 @@ bool ScriptInterface::LoadGlobalScriptFile(const VfsPath& path) const
JS::RootedValue rval(rq.cx);
JS::CompileOptions opts(rq.cx);
opts.setFileAndLine(filenameStr.c_str(), lineNo);
return JS::Evaluate(rq.cx, opts,
reinterpret_cast<const char16_t*>(codeUtf16.c_str()), (uint)(codeUtf16.length()), &rval);
if (JS::Evaluate(rq.cx, opts, reinterpret_cast<const char16_t*>(codeUtf16.c_str()), (uint)(codeUtf16.length()), &rval))
return true;
ScriptException::CatchPending(rq);
return false;
}
bool ScriptInterface::Eval(const char* code) const
{
Request rq(this);
ScriptRequest rq(this);
JS::RootedValue rval(rq.cx);
return Eval_(code, &rval);
}
bool ScriptInterface::Eval_(const char* code, JS::MutableHandleValue rval) const
{
Request rq(this);
ScriptRequest rq(this);
utf16string codeUtf16(code, code+strlen(code));
JS::CompileOptions opts(rq.cx);
opts.setFileAndLine("(eval)", 1);
return JS::Evaluate(rq.cx, opts, reinterpret_cast<const char16_t*>(codeUtf16.c_str()), (uint)codeUtf16.length(), rval);
if (JS::Evaluate(rq.cx, opts, reinterpret_cast<const char16_t*>(codeUtf16.c_str()), (uint)codeUtf16.length(), rval))
return true;
ScriptException::CatchPending(rq);
return false;
}
bool ScriptInterface::Eval_(const wchar_t* code, JS::MutableHandleValue rval) const
{
Request rq(this);
ScriptRequest rq(this);
utf16string codeUtf16(code, code+wcslen(code));
JS::CompileOptions opts(rq.cx);
opts.setFileAndLine("(eval)", 1);
return JS::Evaluate(rq.cx, opts, reinterpret_cast<const char16_t*>(codeUtf16.c_str()), (uint)codeUtf16.length(), rval);
if (JS::Evaluate(rq.cx, opts, reinterpret_cast<const char16_t*>(codeUtf16.c_str()), (uint)codeUtf16.length(), rval))
return true;
ScriptException::CatchPending(rq);
return false;
}
bool ScriptInterface::ParseJSON(const std::string& string_utf8, JS::MutableHandleValue out) const
{
Request rq(this);
ScriptRequest rq(this);
std::wstring attrsW = wstring_from_utf8(string_utf8);
utf16string string(attrsW.begin(), attrsW.end());
if (JS_ParseJSON(rq.cx, reinterpret_cast<const char16_t*>(string.c_str()), (u32)string.size(), out))
return true;
LOGERROR("JS_ParseJSON failed!");
if (!JS_IsExceptionPending(rq.cx))
return false;
JS::RootedValue exc(rq.cx);
if (!JS_GetPendingException(rq.cx, &exc))
return false;
JS_ClearPendingException(rq.cx);
// We expect an object of type SyntaxError
if (!exc.isObject())
return false;
JS::RootedValue rval(rq.cx);
JS::RootedObject excObj(rq.cx, &exc.toObject());
if (!JS_CallFunctionName(rq.cx, excObj, "toString", JS::HandleValueArray::empty(), &rval))
return false;
std::wstring error;
ScriptInterface::FromJSVal(rq, rval, error);
LOGERROR("%s", utf8_from_wstring(error));
ScriptException::CatchPending(rq);
return false;
}
@@ -946,13 +944,12 @@ struct Stringifier
// It probably has historical reasons and could be changed by SpiderMonkey in the future.
std::string ScriptInterface::StringifyJSON(JS::MutableHandleValue obj, bool indent) const
{
Request rq(this);
ScriptRequest rq(this);
Stringifier str;
JS::RootedValue indentVal(rq.cx, indent ? JS::Int32Value(2) : JS::UndefinedValue());
if (!JS_Stringify(rq.cx, obj, nullptr, indentVal, &Stringifier::callback, &str))
{
JS_ClearPendingException(rq.cx);
LOGERROR("StringifyJSON failed");
ScriptException::CatchPending(rq);
return std::string();
}
@@ -962,7 +959,7 @@ std::string ScriptInterface::StringifyJSON(JS::MutableHandleValue obj, bool inde
std::string ScriptInterface::ToString(JS::MutableHandleValue obj, bool pretty) const
{
Request rq(this);
ScriptRequest rq(this);
if (obj.isUndefined())
return "(void 0)";
@@ -985,7 +982,7 @@ std::string ScriptInterface::ToString(JS::MutableHandleValue obj, bool pretty) c
if (ok)
return str.stream.str();
// Clear the exception set when Stringify failed
// Drop exceptions raised by cyclic values before trying something else
JS_ClearPendingException(rq.cx);
}
@@ -997,29 +994,10 @@ std::string ScriptInterface::ToString(JS::MutableHandleValue obj, bool pretty) c
return utf8_from_wstring(source);
}
void ScriptInterface::ReportError(const char* msg) const
{
Request rq(this);
// JS_ReportError by itself doesn't seem to set a JS-style exception, and so
// script callers will be unable to catch anything. So use JS_SetPendingException
// to make sure there really is a script-level exception. But just set it to undefined
// because there's not much value yet in throwing a real exception object.
JS_SetPendingException(rq.cx, JS::UndefinedHandleValue);
// And report the actual error
JS_ReportError(rq.cx, "%s", msg);
// TODO: Why doesn't JS_ReportPendingException(cx); work?
}
bool ScriptInterface::IsExceptionPending(const Request& rq)
{
return JS_IsExceptionPending(rq.cx) ? true : false;
}
JS::Value ScriptInterface::CloneValueFromOtherCompartment(const ScriptInterface& otherCompartment, JS::HandleValue val) const
{
PROFILE("CloneValueFromOtherCompartment");
Request rq(this);
ScriptRequest rq(this);
JS::RootedValue out(rq.cx);
shared_ptr<StructuredClone> structuredClone = otherCompartment.WriteStructuredClone(val);
ReadStructuredClone(structuredClone, &out);
@@ -1039,12 +1017,13 @@ ScriptInterface::StructuredClone::~StructuredClone()
shared_ptr<ScriptInterface::StructuredClone> ScriptInterface::WriteStructuredClone(JS::HandleValue v) const
{
Request rq(this);
ScriptRequest rq(this);
u64* data = NULL;
size_t nbytes = 0;
if (!JS_WriteStructuredClone(rq.cx, v, &data, &nbytes, NULL, NULL, JS::UndefinedHandleValue))
{
debug_warn(L"Writing a structured clone with JS_WriteStructuredClone failed!");
ScriptException::CatchPending(rq);
return shared_ptr<StructuredClone>();
}
@@ -1056,6 +1035,7 @@ shared_ptr<ScriptInterface::StructuredClone> ScriptInterface::WriteStructuredClo
void ScriptInterface::ReadStructuredClone(const shared_ptr<ScriptInterface::StructuredClone>& ptr, JS::MutableHandleValue ret) const
{
Request rq(this);
JS_ReadStructuredClone(rq.cx, ptr->m_Data, ptr->m_Size, JS_STRUCTURED_CLONE_VERSION, ret, NULL, NULL);
ScriptRequest rq(this);
if (!JS_ReadStructuredClone(rq.cx, ptr->m_Data, ptr->m_Size, JS_STRUCTURED_CLONE_VERSION, ret, NULL, NULL))
ScriptException::CatchPending(rq);
}
+67 -69
View File
@@ -20,8 +20,9 @@
#include "lib/file/vfs/vfs_path.h"
#include "maths/Fixed.h"
#include "ScriptTypes.h"
#include "ps/Errors.h"
#include "scriptinterface/ScriptExceptions.h"
#include "scriptinterface/ScriptTypes.h"
#include <boost/random/linear_congruential.hpp>
#include <map>
@@ -48,6 +49,7 @@ ERROR_TYPE(Scripting_DefineType, CreationFailed);
// but as large as necessary for all wrapped functions)
#define SCRIPT_INTERFACE_MAX_ARGS 8
class ScriptInterface;
struct ScriptInterface_impl;
class ScriptContext;
@@ -56,6 +58,32 @@ class ScriptContext;
// use their own threads and also their own contexts.
extern thread_local shared_ptr<ScriptContext> g_ScriptContext;
/**
* RAII structure which encapsulates an access to the context and compartment of a ScriptInterface.
* This struct provides:
* - a pointer to the context, while acting like JSAutoRequest
* - a pointer to the global object of the compartment, while acting like JSAutoCompartment
*
* This way, getting and using those pointers is safe with respect to the GC
* and to the separation of compartments.
*/
class ScriptRequest
{
public:
ScriptRequest() = delete;
ScriptRequest(const ScriptRequest& rq) = delete;
ScriptRequest& operator=(const ScriptRequest& rq) = delete;
ScriptRequest(const ScriptInterface& scriptInterface);
ScriptRequest(const ScriptInterface* scriptInterface) : ScriptRequest(*scriptInterface) {}
ScriptRequest(shared_ptr<ScriptInterface> scriptInterface) : ScriptRequest(*scriptInterface) {}
~ScriptRequest();
JS::Value globalValue() const;
JSContext* cx;
JSObject* glob;
private:
JSCompartment* m_formerCompartment;
};
/**
* Abstraction around a SpiderMonkey JSCompartment.
@@ -69,6 +97,7 @@ class ScriptInterface
{
NONCOPYABLE(ScriptInterface);
friend class ScriptRequest;
public:
/**
@@ -94,34 +123,6 @@ public:
JSRuntime* GetJSRuntime() const;
shared_ptr<ScriptContext> GetContext() const;
/**
* RAII structure which encapsulates an access to the context and compartment of a ScriptInterface.
* This struct provides:
* - a pointer to the context, while acting like JSAutoRequest
* - a pointer to the global object of the compartment, while acting like JSAutoCompartment
*
* This way, getting and using those pointers is safe with respect to the GC
* and to the separation of compartments.
*/
struct Request
{
Request() = delete;
Request(const Request& rq) = delete;
Request& operator=(const Request& rq) = delete;
Request(const ScriptInterface& scriptInterface);
Request(const ScriptInterface* scriptInterface) : Request(*scriptInterface) {}
Request(shared_ptr<ScriptInterface> scriptInterface) : Request(*scriptInterface) {}
Request(const CmptPrivate* cmptPrivate) : Request(cmptPrivate->pScriptInterface) {}
~Request();
JS::Value globalValue() const;
JSContext* cx;
JSObject* glob;
private:
JSCompartment* m_formerCompartment;
};
friend struct Request;
/**
* Load global scripts that most script interfaces need,
* located in the /globalscripts directory. VFS must be initialized.
@@ -150,7 +151,7 @@ public:
* Can throw an exception.
*/
template<typename... Args>
static bool CreateObject(const Request& rq, JS::MutableHandleValue objectValue, Args const&... args)
static bool CreateObject(const ScriptRequest& rq, JS::MutableHandleValue objectValue, Args const&... args)
{
JS::RootedObject obj(rq.cx);
@@ -164,7 +165,7 @@ public:
/**
* Sets the given value to a new JS object or Null Value in case of out-of-memory.
*/
static void CreateArray(const Request& rq, JS::MutableHandleValue objectValue, size_t length = 0);
static void CreateArray(const ScriptRequest& rq, JS::MutableHandleValue objectValue, size_t length = 0);
/**
* Set the named property on the global object.
@@ -267,13 +268,6 @@ public:
*/
std::string StringifyJSON(JS::MutableHandleValue obj, bool indent = true) const;
/**
* Report the given error message through the JS error reporting mechanism,
* and throw a JS exception. (Callers can check IsPendingException, and must
* return false in that case to propagate the exception.)
*/
void ReportError(const char* msg) const;
/**
* Load and execute the given script in a new function scope.
* @param filename Name for debugging purposes (not used to load the file)
@@ -307,7 +301,7 @@ public:
/**
* Convert a JS::Value to a C++ type. (This might trigger GC.)
*/
template<typename T> static bool FromJSVal(const Request& rq, const JS::HandleValue val, T& ret);
template<typename T> static bool FromJSVal(const ScriptRequest& rq, const JS::HandleValue val, T& ret);
/**
* Convert a C++ type to a JS::Value. (This might trigger GC. The return
@@ -316,12 +310,12 @@ public:
* The reason is a memory corruption problem that appears to be caused by a bug in Visual Studio.
* Details here: http://www.wildfiregames.com/forum/index.php?showtopic=17289&p=285921
*/
template<typename T> static void ToJSVal(const Request& rq, JS::MutableHandleValue ret, T const& val);
template<typename T> static void ToJSVal(const ScriptRequest& rq, JS::MutableHandleValue ret, T const& val);
/**
* Convert a named property of an object to a C++ type.
*/
template<typename T> static bool FromJSProperty(const Request& rq, const JS::HandleValue val, const char* name, T& ret, bool strict = false);
template<typename T> static bool FromJSProperty(const ScriptRequest& rq, const JS::HandleValue val, const char* name, T& ret, bool strict = false);
/**
* MathRandom (this function) calls the random number generator assigned to this ScriptInterface instance and
@@ -355,11 +349,13 @@ public:
* Retrieve the private data field of a JSObject that is an instance of the given JSClass.
*/
template <typename T>
static T* GetPrivate(const Request& rq, JS::HandleObject thisobj, JSClass* jsClass)
static T* GetPrivate(const ScriptRequest& rq, JS::HandleObject thisobj, JSClass* jsClass)
{
T* value = static_cast<T*>(JS_GetInstancePrivate(rq.cx, thisobj, jsClass, nullptr));
if (value == nullptr && !JS_IsExceptionPending(rq.cx))
JS_ReportError(rq.cx, "Private data of the given object is null!");
if (value == nullptr)
ScriptException::Raise(rq, "Private data of the given object is null!");
return value;
}
@@ -368,17 +364,20 @@ public:
* If an error occurs, GetPrivate will report it with the according stack.
*/
template <typename T>
static T* GetPrivate(const Request& rq, JS::CallArgs& callArgs, JSClass* jsClass)
static T* GetPrivate(const ScriptRequest& rq, JS::CallArgs& callArgs, JSClass* jsClass)
{
if (!callArgs.thisv().isObject())
{
JS_ReportError(rq.cx, "Cannot retrieve private JS class data because from a non-object value!");
ScriptException::Raise(rq, "Cannot retrieve private JS class data because from a non-object value!");
return nullptr;
}
JS::RootedObject thisObj(rq.cx, &callArgs.thisv().toObject());
T* value = static_cast<T*>(JS_GetInstancePrivate(rq.cx, thisObj, jsClass, &callArgs));
if (value == nullptr && !JS_IsExceptionPending(rq.cx))
JS_ReportError(rq.cx, "Private data of the given object is null!");
if (value == nullptr)
ScriptException::Raise(rq, "Private data of the given object is null!");
return value;
}
@@ -391,7 +390,7 @@ public:
* because "conversions" from JS::HandleValue to JS::MutableHandleValue are unusual and should not happen "by accident".
*/
template <typename T>
static void AssignOrToJSVal(const Request& rq, JS::MutableHandleValue handle, const T& a);
static void AssignOrToJSVal(const ScriptRequest& rq, JS::MutableHandleValue handle, const T& a);
/**
* The same as AssignOrToJSVal, but also allows JS::Value for T.
@@ -401,7 +400,7 @@ public:
* "unrooted" version of AssignOrToJSVal.
*/
template <typename T>
static void AssignOrToJSValUnrooted(const Request& rq, JS::MutableHandleValue handle, const T& a)
static void AssignOrToJSValUnrooted(const ScriptRequest& rq, JS::MutableHandleValue handle, const T& a)
{
AssignOrToJSVal(rq, handle, a);
}
@@ -412,14 +411,14 @@ public:
* other types.
*/
template <typename T>
static T AssignOrFromJSVal(const Request& rq, const JS::HandleValue& val, bool& ret);
static T AssignOrFromJSVal(const ScriptRequest& rq, const JS::HandleValue& val, bool& ret);
private:
static bool CreateObject_(const Request& rq, JS::MutableHandleObject obj);
static bool CreateObject_(const ScriptRequest& rq, JS::MutableHandleObject obj);
template<typename T, typename... Args>
static bool CreateObject_(const Request& rq, JS::MutableHandleObject obj, const char* propertyName, const T& propertyValue, Args const&... args)
static bool CreateObject_(const ScriptRequest& rq, JS::MutableHandleObject obj, const char* propertyName, const T& propertyValue, Args const&... args)
{
JS::RootedValue val(rq.cx);
AssignOrToJSVal(rq, &val, propertyValue);
@@ -436,7 +435,6 @@ private:
bool SetPropertyInt_(JS::HandleValue obj, int name, JS::HandleValue value, bool constant, bool enumerate) const;
bool GetProperty_(JS::HandleValue obj, const char* name, JS::MutableHandleValue out) const;
bool GetPropertyInt_(JS::HandleValue obj, int name, JS::MutableHandleValue value) const;
static bool IsExceptionPending(const Request& rq);
struct CustomType
{
@@ -490,43 +488,43 @@ public:
#include "NativeWrapperDefns.h"
template<typename T>
inline void ScriptInterface::AssignOrToJSVal(const Request& rq, JS::MutableHandleValue handle, const T& a)
inline void ScriptInterface::AssignOrToJSVal(const ScriptRequest& rq, JS::MutableHandleValue handle, const T& a)
{
ToJSVal(rq, handle, a);
}
template<>
inline void ScriptInterface::AssignOrToJSVal<JS::PersistentRootedValue>(const Request& UNUSED(rq), JS::MutableHandleValue handle, const JS::PersistentRootedValue& a)
inline void ScriptInterface::AssignOrToJSVal<JS::PersistentRootedValue>(const ScriptRequest& UNUSED(rq), JS::MutableHandleValue handle, const JS::PersistentRootedValue& a)
{
handle.set(a);
}
template<>
inline void ScriptInterface::AssignOrToJSVal<JS::Heap<JS::Value> >(const Request& UNUSED(rq), JS::MutableHandleValue handle, const JS::Heap<JS::Value>& a)
inline void ScriptInterface::AssignOrToJSVal<JS::Heap<JS::Value> >(const ScriptRequest& UNUSED(rq), JS::MutableHandleValue handle, const JS::Heap<JS::Value>& a)
{
handle.set(a);
}
template<>
inline void ScriptInterface::AssignOrToJSVal<JS::RootedValue>(const Request& UNUSED(rq), JS::MutableHandleValue handle, const JS::RootedValue& a)
inline void ScriptInterface::AssignOrToJSVal<JS::RootedValue>(const ScriptRequest& UNUSED(rq), JS::MutableHandleValue handle, const JS::RootedValue& a)
{
handle.set(a);
}
template <>
inline void ScriptInterface::AssignOrToJSVal<JS::HandleValue>(const Request& UNUSED(rq), JS::MutableHandleValue handle, const JS::HandleValue& a)
inline void ScriptInterface::AssignOrToJSVal<JS::HandleValue>(const ScriptRequest& UNUSED(rq), JS::MutableHandleValue handle, const JS::HandleValue& a)
{
handle.set(a);
}
template <>
inline void ScriptInterface::AssignOrToJSValUnrooted<JS::Value>(const Request& UNUSED(rq), JS::MutableHandleValue handle, const JS::Value& a)
inline void ScriptInterface::AssignOrToJSValUnrooted<JS::Value>(const ScriptRequest& UNUSED(rq), JS::MutableHandleValue handle, const JS::Value& a)
{
handle.set(a);
}
template<typename T>
inline T ScriptInterface::AssignOrFromJSVal(const Request& rq, const JS::HandleValue& val, bool& ret)
inline T ScriptInterface::AssignOrFromJSVal(const ScriptRequest& rq, const JS::HandleValue& val, bool& ret)
{
T retVal;
ret = FromJSVal(rq, val, retVal);
@@ -534,7 +532,7 @@ inline T ScriptInterface::AssignOrFromJSVal(const Request& rq, const JS::HandleV
}
template<>
inline JS::HandleValue ScriptInterface::AssignOrFromJSVal<JS::HandleValue>(const Request& UNUSED(rq), const JS::HandleValue& val, bool& ret)
inline JS::HandleValue ScriptInterface::AssignOrFromJSVal<JS::HandleValue>(const ScriptRequest& UNUSED(rq), const JS::HandleValue& val, bool& ret)
{
ret = true;
return val;
@@ -543,7 +541,7 @@ inline JS::HandleValue ScriptInterface::AssignOrFromJSVal<JS::HandleValue>(const
template<typename T>
bool ScriptInterface::SetGlobal(const char* name, const T& value, bool replace, bool constant, bool enumerate)
{
Request rq(this);
ScriptRequest rq(this);
JS::RootedValue val(rq.cx);
AssignOrToJSVal(rq, &val, value);
return SetGlobal_(name, val, replace, constant, enumerate);
@@ -552,7 +550,7 @@ bool ScriptInterface::SetGlobal(const char* name, const T& value, bool replace,
template<typename T>
bool ScriptInterface::SetProperty(JS::HandleValue obj, const char* name, const T& value, bool constant, bool enumerate) const
{
Request rq(this);
ScriptRequest rq(this);
JS::RootedValue val(rq.cx);
AssignOrToJSVal(rq, &val, value);
return SetProperty_(obj, name, val, constant, enumerate);
@@ -561,7 +559,7 @@ bool ScriptInterface::SetProperty(JS::HandleValue obj, const char* name, const T
template<typename T>
bool ScriptInterface::SetProperty(JS::HandleValue obj, const wchar_t* name, const T& value, bool constant, bool enumerate) const
{
Request rq(this);
ScriptRequest rq(this);
JS::RootedValue val(rq.cx);
AssignOrToJSVal(rq, &val, value);
return SetProperty_(obj, name, val, constant, enumerate);
@@ -570,7 +568,7 @@ bool ScriptInterface::SetProperty(JS::HandleValue obj, const wchar_t* name, cons
template<typename T>
bool ScriptInterface::SetPropertyInt(JS::HandleValue obj, int name, const T& value, bool constant, bool enumerate) const
{
Request rq(this);
ScriptRequest rq(this);
JS::RootedValue val(rq.cx);
AssignOrToJSVal(rq, &val, value);
return SetPropertyInt_(obj, name, val, constant, enumerate);
@@ -579,7 +577,7 @@ bool ScriptInterface::SetPropertyInt(JS::HandleValue obj, int name, const T& val
template<typename T>
bool ScriptInterface::GetProperty(JS::HandleValue obj, const char* name, T& out) const
{
Request rq(this);
ScriptRequest rq(this);
JS::RootedValue val(rq.cx);
if (!GetProperty_(obj, name, &val))
return false;
@@ -589,7 +587,7 @@ bool ScriptInterface::GetProperty(JS::HandleValue obj, const char* name, T& out)
template<typename T>
bool ScriptInterface::GetPropertyInt(JS::HandleValue obj, int name, T& out) const
{
Request rq(this);
ScriptRequest rq(this);
JS::RootedValue val(rq.cx);
if (!GetPropertyInt_(obj, name, &val))
return false;
@@ -607,7 +605,7 @@ bool ScriptInterface::Eval(const CHAR* code, JS::MutableHandleValue ret) const
template<typename T, typename CHAR>
bool ScriptInterface::Eval(const CHAR* code, T& ret) const
{
Request rq(this);
ScriptRequest rq(this);
JS::RootedValue rval(rq.cx);
if (!Eval_(code, &rval))
return false;
@@ -35,7 +35,7 @@ class TestScriptConversions : public CxxTest::TestSuite
{
ScriptInterface script("Test", "Test", g_ScriptContext);
TS_ASSERT(script.LoadGlobalScripts());
ScriptInterface::Request rq(script);
ScriptRequest rq(script);
JS::RootedValue v1(rq.cx);
ScriptInterface::ToJSVal(rq, &v1, value);
@@ -54,7 +54,7 @@ class TestScriptConversions : public CxxTest::TestSuite
{
ScriptInterface script("Test", "Test", g_ScriptContext);
TS_ASSERT(script.LoadGlobalScripts());
ScriptInterface::Request rq(script);
ScriptRequest rq(script);
JS::RootedValue v1(rq.cx);
ScriptInterface::ToJSVal(rq, &v1, value);
@@ -76,7 +76,7 @@ class TestScriptConversions : public CxxTest::TestSuite
{
ScriptInterface script("Test", "Test", g_ScriptContext);
TS_ASSERT(script.LoadGlobalScripts());
ScriptInterface::Request rq(script);
ScriptRequest rq(script);
JS::RootedValue v1(rq.cx);
ScriptInterface::ToJSVal(rq, &v1, v);
@@ -169,7 +169,7 @@ public:
void test_integers()
{
ScriptInterface script("Test", "Test", g_ScriptContext);
ScriptInterface::Request rq(script);
ScriptRequest rq(script);
// using new uninitialized variables each time to be sure the test doesn't succeeed if ToJSVal doesn't touch the value at all.
JS::RootedValue val0(rq.cx), val1(rq.cx), val2(rq.cx), val3(rq.cx), val4(rq.cx), val5(rq.cx), val6(rq.cx), val7(rq.cx), val8(rq.cx);
@@ -201,7 +201,7 @@ public:
convert_to<float>(std::numeric_limits<float>::quiet_NaN(), "NaN"); // can't use roundtrip since nan != nan
ScriptInterface script("Test", "Test", g_ScriptContext);
ScriptInterface::Request rq(script);
ScriptRequest rq(script);
float f = 0;
JS::RootedValue testNANVal(rq.cx);
@@ -252,7 +252,7 @@ public:
// Fancier conversion: we store UTF8 and get UTF16 and vice-versa
ScriptInterface script("Test", "Test", g_ScriptContext);
TS_ASSERT(script.LoadGlobalScripts());
ScriptInterface::Request rq(script);
ScriptRequest rq(script);
std::string in_utf8("éè!§$-aezi134900°°©©¢¢ÇÇ‘{¶«¡Ç’[å»ÛÁØ");
std::wstring in_utf16(L"éè!§$-aezi134900°°©©¢¢ÇÇ‘{¶«¡Ç’[å»ÛÁØ");
@@ -40,7 +40,7 @@ public:
ScriptInterface script("Test", "Test", g_ScriptContext);
TestLogger logger;
TS_ASSERT(!script.LoadScript(L"test.js", "1+"));
TS_ASSERT_STR_CONTAINS(logger.GetOutput(), "JavaScript error: test.js line 1\nSyntaxError: expected expression, got end of script");
TS_ASSERT_STR_CONTAINS(logger.GetOutput(), "ERROR: JavaScript error: test.js line 1\nSyntaxError: expected expression, got end of script");
}
void test_loadscript_strict_warning()
@@ -57,7 +57,7 @@ public:
ScriptInterface script("Test", "Test", g_ScriptContext);
TestLogger logger;
TS_ASSERT(!script.LoadScript(L"test.js", "with(1){}"));
TS_ASSERT_STR_CONTAINS(logger.GetOutput(), "JavaScript error: test.js line 1\nSyntaxError: strict mode code may not contain \'with\' statements");
TS_ASSERT_STR_CONTAINS(logger.GetOutput(), "ERROR: JavaScript error: test.js line 1\nSyntaxError: strict mode code may not contain \'with\' statements");
}
void test_clone_basic()
@@ -65,12 +65,12 @@ public:
ScriptInterface script1("Test", "Test", g_ScriptContext);
ScriptInterface script2("Test", "Test", g_ScriptContext);
ScriptInterface::Request rq1(script1);
ScriptRequest rq1(script1);
JS::RootedValue obj1(rq1.cx);
TS_ASSERT(script1.Eval("({'x': 123, 'y': [1, 1.5, '2', 'test', undefined, null, true, false]})", &obj1));
{
ScriptInterface::Request rq2(script2);
ScriptRequest rq2(script2);
JS::RootedValue obj2(rq2.cx, script2.CloneValueFromOtherCompartment(script1, obj1));
@@ -86,13 +86,13 @@ public:
ScriptInterface script1("Test", "Test", g_ScriptContext);
ScriptInterface script2("Test", "Test", g_ScriptContext);
ScriptInterface::Request rq1(script1);
ScriptRequest rq1(script1);
JS::RootedValue obj1(rq1.cx);
TS_ASSERT(script1.Eval("var s = '?'; var v = ({get x() { return 123 }, 'y': {'w':{get z() { delete v.y; delete v.n; v = null; s += s; return 4 }}}, 'n': 100}); v", &obj1));
{
ScriptInterface::Request rq2(script2);
ScriptRequest rq2(script2);
JS::RootedValue obj2(rq2.cx, script2.CloneValueFromOtherCompartment(script1, obj1));
@@ -107,13 +107,13 @@ public:
ScriptInterface script1("Test", "Test", g_ScriptContext);
ScriptInterface script2("Test", "Test", g_ScriptContext);
ScriptInterface::Request rq1(script1);
ScriptRequest rq1(script1);
JS::RootedValue obj1(rq1.cx);
TS_ASSERT(script1.Eval("var x = []; x[0] = x; ({'a': x, 'b': x})", &obj1));
{
ScriptInterface::Request rq2(script2);
ScriptRequest rq2(script2);
JS::RootedValue obj2(rq2.cx, script2.CloneValueFromOtherCompartment(script1, obj1));
// Use JSAPI function to check if the values of the properties "a", "b" are equals a.x[0]
@@ -138,7 +138,7 @@ public:
{
ScriptInterface script("Test", "Test", g_ScriptContext);
ScriptInterface::Request rq(script);
ScriptRequest rq(script);
JS::RootedValue val(rq.cx);
JS::RootedValue out(rq.cx);
@@ -183,7 +183,7 @@ public:
void handle_templates_test(const ScriptInterface& script, JS::HandleValue val, JS::MutableHandleValue out, JS::HandleValue nbrVal)
{
ScriptInterface::Request rq(script);
ScriptRequest rq(script);
int nbr = 0;
@@ -236,7 +236,7 @@ public:
void test_json()
{
ScriptInterface script("Test", "Test", g_ScriptContext);
ScriptInterface::Request rq(script);
ScriptRequest rq(script);
std::string input = "({'x':1,'z':[2,'3\\u263A\\ud800'],\"y\":true})";
JS::RootedValue val(rq.cx);
@@ -254,7 +254,7 @@ public:
void test_function_override()
{
ScriptInterface script("Test", "Test", g_ScriptContext);
ScriptInterface::Request rq(script);
ScriptRequest rq(script);
TS_ASSERT(script.Eval(
"function f() { return 1; }"