mirror of
https://gitea.wildfiregames.com/0ad/0ad.git
synced 2026-09-21 20:06:40 +00:00
More exact stack rooting (CallFunction object).
Changes CallFunction and CallFunctionVoid to use a HandleValue as object parameter. Also changes some JS serialization/deserialization functions to only support the JSAPI rooted types (drop support for CScriptVal and CScriptValRooted there). Some other functions got changed too because they were closely related. Refs #2415 Refs #2462 This was SVN commit r15592.
This commit is contained in:
@@ -74,7 +74,7 @@ BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
|
||||
// Call the named property on the given object
|
||||
#define OVERLOADS(z, i, data) \
|
||||
template <typename R TYPENAME_T0_TAIL(z, i)> \
|
||||
bool CallFunction(jsval val, const char* name, T0_A0_CONST_REF(z,i) R& ret);
|
||||
bool CallFunction(JS::HandleValue val, const char* name, T0_A0_CONST_REF(z,i) R& ret);
|
||||
BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
|
||||
#undef OVERLOADS
|
||||
|
||||
@@ -83,7 +83,7 @@ BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
|
||||
// (as people would expect it to work based on the SpiderMonkey rooting guide).
|
||||
#define OVERLOADS(z, i, data) \
|
||||
template <typename R TYPENAME_T0_TAIL(z, i)> \
|
||||
bool CallFunction(jsval val, const char* name, T0_A0_CONST_REF(z,i) JS::Rooted<R>* ret);
|
||||
bool CallFunction(JS::HandleValue val, const char* name, T0_A0_CONST_REF(z,i) JS::Rooted<R>* ret);
|
||||
BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
|
||||
#undef OVERLOADS
|
||||
|
||||
@@ -91,7 +91,7 @@ BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
|
||||
// without requiring implicit conversion.
|
||||
#define OVERLOADS(z, i, data) \
|
||||
template <typename R TYPENAME_T0_TAIL(z, i)> \
|
||||
bool CallFunction(jsval val, const char* name, T0_A0_CONST_REF(z,i) JS::MutableHandle<R> ret);
|
||||
bool CallFunction(JS::HandleValue val, const char* name, T0_A0_CONST_REF(z,i) JS::MutableHandle<R> ret);
|
||||
BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
|
||||
#undef OVERLOADS
|
||||
|
||||
|
||||
@@ -129,16 +129,15 @@ BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
|
||||
|
||||
#define OVERLOADS(z, i, data) \
|
||||
template<typename R TYPENAME_T0_TAIL(z, i)> \
|
||||
bool ScriptInterface::CallFunction(jsval val, const char* name, T0_A0_CONST_REF(z,i) R& ret) \
|
||||
bool ScriptInterface::CallFunction(JS::HandleValue val, const char* name, T0_A0_CONST_REF(z,i) R& ret) \
|
||||
{ \
|
||||
JSContext* cx = GetContext(); \
|
||||
JSAutoRequest rq(cx); \
|
||||
JS::RootedValue jsRet(cx); \
|
||||
JS::RootedValue val1(cx, val); \
|
||||
JS::AutoValueVector argv(cx); \
|
||||
argv.resize(i); \
|
||||
BOOST_PP_REPEAT_##z (i, ASSIGN_OR_TO_JS_VAL, ~) \
|
||||
bool ok = CallFunction_(val1, name, argv.length(), argv.begin(), &jsRet); \
|
||||
bool ok = CallFunction_(val, name, argv.length(), argv.begin(), &jsRet); \
|
||||
if (!ok) \
|
||||
return false; \
|
||||
return FromJSVal(cx, jsRet, ret); \
|
||||
@@ -148,16 +147,15 @@ BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
|
||||
|
||||
#define OVERLOADS(z, i, data) \
|
||||
template<typename R TYPENAME_T0_TAIL(z, i)> \
|
||||
bool ScriptInterface::CallFunction(jsval val, const char* name, T0_A0_CONST_REF(z,i) JS::Rooted<R>* ret) \
|
||||
bool ScriptInterface::CallFunction(JS::HandleValue val, const char* name, T0_A0_CONST_REF(z,i) JS::Rooted<R>* ret) \
|
||||
{ \
|
||||
JSContext* cx = GetContext(); \
|
||||
JSAutoRequest rq(cx); \
|
||||
JS::MutableHandle<R> jsRet(ret); \
|
||||
JS::RootedValue val1(cx, val); \
|
||||
JS::AutoValueVector argv(cx); \
|
||||
argv.resize(i); \
|
||||
BOOST_PP_REPEAT_##z (i, ASSIGN_OR_TO_JS_VAL, ~) \
|
||||
bool ok = CallFunction_(val1, name, argv.length(), argv.begin(), jsRet); \
|
||||
bool ok = CallFunction_(val, name, argv.length(), argv.begin(), jsRet); \
|
||||
if (!ok) \
|
||||
return false; \
|
||||
return true; \
|
||||
@@ -167,15 +165,14 @@ BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
|
||||
|
||||
#define OVERLOADS(z, i, data) \
|
||||
template<typename R TYPENAME_T0_TAIL(z, i)> \
|
||||
bool ScriptInterface::CallFunction(jsval val, const char* name, T0_A0_CONST_REF(z,i) JS::MutableHandle<R> ret) \
|
||||
bool ScriptInterface::CallFunction(JS::HandleValue val, const char* name, T0_A0_CONST_REF(z,i) JS::MutableHandle<R> ret) \
|
||||
{ \
|
||||
JSContext* cx = GetContext(); \
|
||||
JSAutoRequest rq(cx); \
|
||||
JS::RootedValue val1(cx, val); \
|
||||
JS::AutoValueVector argv(cx); \
|
||||
argv.resize(i); \
|
||||
BOOST_PP_REPEAT_##z (i, ASSIGN_OR_TO_JS_VAL, ~) \
|
||||
bool ok = CallFunction_(val1, name, argv.length(), argv.begin(), ret); \
|
||||
bool ok = CallFunction_(val, name, argv.length(), argv.begin(), ret); \
|
||||
if (!ok) \
|
||||
return false; \
|
||||
return true; \
|
||||
|
||||
@@ -964,12 +964,11 @@ JSObject* ScriptInterface::CreateCustomObject(const std::string & typeName)
|
||||
return JS_NewObject(m->m_cx, (*it).second.m_Class, prototype, NULL);
|
||||
}
|
||||
|
||||
bool ScriptInterface::CallFunctionVoid(jsval val, const char* name)
|
||||
bool ScriptInterface::CallFunctionVoid(JS::HandleValue val, const char* name)
|
||||
{
|
||||
JSAutoRequest rq(m->m_cx);
|
||||
JS::RootedValue jsRet(m->m_cx);
|
||||
JS::RootedValue val1(m->m_cx, val);
|
||||
return CallFunction_(val1, name, 0, NULL, &jsRet);
|
||||
return CallFunction_(val, name, 0, NULL, &jsRet);
|
||||
}
|
||||
|
||||
bool ScriptInterface::CallFunction_(JS::HandleValue val, const char* name, uint argc, jsval* argv, JS::MutableHandleValue ret)
|
||||
@@ -1361,7 +1360,8 @@ std::string ScriptInterface::StringifyJSON(jsval obj, bool indent)
|
||||
std::wstring ScriptInterface::ToString(jsval obj, bool pretty)
|
||||
{
|
||||
JSAutoRequest rq(m->m_cx);
|
||||
if (JSVAL_IS_VOID(obj))
|
||||
|
||||
if (obj.isUndefined())
|
||||
return L"(void 0)";
|
||||
|
||||
// Try to stringify as JSON if possible
|
||||
@@ -1373,7 +1373,7 @@ std::wstring ScriptInterface::ToString(jsval obj, bool pretty)
|
||||
// Temporary disable the error reporter, so we don't print complaints about cyclic values
|
||||
JSErrorReporter er = JS_SetErrorReporter(m->m_cx, NULL);
|
||||
|
||||
JSBool ok = JS_Stringify(m->m_cx, &obj, NULL, INT_TO_JSVAL(2), &StringifierW::callback, &str);
|
||||
JSBool ok = JS_Stringify(m->m_cx, &obj, NULL, JS::NumberValue(2), &StringifierW::callback, &str);
|
||||
|
||||
// Restore error reporter
|
||||
JS_SetErrorReporter(m->m_cx, er);
|
||||
@@ -1389,7 +1389,8 @@ std::wstring ScriptInterface::ToString(jsval obj, bool pretty)
|
||||
// so fall back to obj.toSource()
|
||||
|
||||
std::wstring source = L"(error)";
|
||||
CallFunction(obj, "toSource", source);
|
||||
JS::RootedValue tmpObj(m->m_cx, obj); // TODO: pass Handle as argument already
|
||||
CallFunction(tmpObj, "toSource", source);
|
||||
return source;
|
||||
}
|
||||
|
||||
|
||||
@@ -150,25 +150,25 @@ public:
|
||||
/**
|
||||
* Call the named property on the given object, with void return type and 0 arguments
|
||||
*/
|
||||
bool CallFunctionVoid(jsval val, const char* name);
|
||||
bool CallFunctionVoid(JS::HandleValue val, const char* name);
|
||||
|
||||
/**
|
||||
* Call the named property on the given object, with void return type and 1 argument
|
||||
*/
|
||||
template<typename T0>
|
||||
bool CallFunctionVoid(jsval val, const char* name, const T0& a0);
|
||||
bool CallFunctionVoid(JS::HandleValue val, const char* name, const T0& a0);
|
||||
|
||||
/**
|
||||
* Call the named property on the given object, with void return type and 2 arguments
|
||||
*/
|
||||
template<typename T0, typename T1>
|
||||
bool CallFunctionVoid(jsval val, const char* name, const T0& a0, const T1& a1);
|
||||
bool CallFunctionVoid(JS::HandleValue val, const char* name, const T0& a0, const T1& a1);
|
||||
|
||||
/**
|
||||
* Call the named property on the given object, with void return type and 3 arguments
|
||||
*/
|
||||
template<typename T0, typename T1, typename T2>
|
||||
bool CallFunctionVoid(jsval val, const char* name, const T0& a0, const T1& a1, const T2& a2);
|
||||
bool CallFunctionVoid(JS::HandleValue val, const char* name, const T0& a0, const T1& a1, const T2& a2);
|
||||
|
||||
JSObject* CreateCustomObject(const std::string & typeName);
|
||||
void DefineCustomObjectType(JSClass *clasp, JSNative constructor, uint minArgs, JSPropertySpec *ps, JSFunctionSpec *fs, JSPropertySpec *static_ps, JSFunctionSpec *static_fs);
|
||||
@@ -480,45 +480,42 @@ inline void ScriptInterface::AssignOrToJSVal<JS::Value>(JS::MutableHandleValue h
|
||||
}
|
||||
|
||||
template<typename T0>
|
||||
bool ScriptInterface::CallFunctionVoid(jsval val, const char* name, const T0& a0)
|
||||
bool ScriptInterface::CallFunctionVoid(JS::HandleValue val, const char* name, const T0& a0)
|
||||
{
|
||||
JSContext* cx = GetContext();
|
||||
JSAutoRequest rq(cx);
|
||||
JS::RootedValue jsRet(cx);
|
||||
JS::RootedValue val1(cx, val);
|
||||
JS::AutoValueVector argv(cx);
|
||||
argv.resize(1);
|
||||
AssignOrToJSVal(argv.handleAt(0), a0);
|
||||
return CallFunction_(val1, name, 1, argv.begin(), &jsRet);
|
||||
return CallFunction_(val, name, 1, argv.begin(), &jsRet);
|
||||
}
|
||||
|
||||
template<typename T0, typename T1>
|
||||
bool ScriptInterface::CallFunctionVoid(jsval val, const char* name, const T0& a0, const T1& a1)
|
||||
bool ScriptInterface::CallFunctionVoid(JS::HandleValue val, const char* name, const T0& a0, const T1& a1)
|
||||
{
|
||||
JSContext* cx = GetContext();
|
||||
JSAutoRequest rq(cx);
|
||||
JS::RootedValue jsRet(cx);
|
||||
JS::RootedValue val1(cx, val);
|
||||
JS::AutoValueVector argv(cx);
|
||||
argv.resize(2);
|
||||
AssignOrToJSVal(argv.handleAt(0), a0);
|
||||
AssignOrToJSVal(argv.handleAt(1), a1);
|
||||
return CallFunction_(val1, name, 2, argv.begin(), &jsRet);
|
||||
return CallFunction_(val, name, 2, argv.begin(), &jsRet);
|
||||
}
|
||||
|
||||
template<typename T0, typename T1, typename T2>
|
||||
bool ScriptInterface::CallFunctionVoid(jsval val, const char* name, const T0& a0, const T1& a1, const T2& a2)
|
||||
bool ScriptInterface::CallFunctionVoid(JS::HandleValue val, const char* name, const T0& a0, const T1& a1, const T2& a2)
|
||||
{
|
||||
JSContext* cx = GetContext();
|
||||
JSAutoRequest rq(cx);
|
||||
JS::RootedValue jsRet(cx);
|
||||
JS::RootedValue val1(cx, val);
|
||||
JS::AutoValueVector argv(cx);
|
||||
argv.resize(3);
|
||||
AssignOrToJSVal(argv.handleAt(0), a0);
|
||||
AssignOrToJSVal(argv.handleAt(1), a1);
|
||||
AssignOrToJSVal(argv.handleAt(2), a2);
|
||||
return CallFunction_(val1, name, 3, argv.begin(), &jsRet);
|
||||
return CallFunction_(val, name, 3, argv.begin(), &jsRet);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
||||
@@ -40,7 +40,8 @@ class TestScriptConversions : public CxxTest::TestSuite
|
||||
// We want to convert values to strings, but can't just call toSource() on them
|
||||
// since they might not be objects. So just use uneval.
|
||||
std::string source;
|
||||
TS_ASSERT(script.CallFunction(JS::ObjectValue(*JS_GetGlobalForScopeChain(cx)), "uneval", CScriptVal(v1), source));
|
||||
JS::RootedValue global(cx, script.GetGlobalObject());
|
||||
TS_ASSERT(script.CallFunction(global, "uneval", v1, source));
|
||||
|
||||
TS_ASSERT_STR_EQUALS(source, expected);
|
||||
}
|
||||
@@ -56,7 +57,8 @@ class TestScriptConversions : public CxxTest::TestSuite
|
||||
ScriptInterface::ToJSVal(cx, &v1, value);
|
||||
|
||||
std::string source;
|
||||
TS_ASSERT(script.CallFunction(JS::ObjectValue(*JS_GetGlobalForScopeChain(cx)), "uneval", CScriptVal(v1), source));
|
||||
JS::RootedValue global(cx, script.GetGlobalObject());
|
||||
TS_ASSERT(script.CallFunction(global, "uneval", v1, source));
|
||||
|
||||
if (expected)
|
||||
TS_ASSERT_STR_EQUALS(source, expected);
|
||||
|
||||
@@ -65,14 +65,21 @@ public:
|
||||
ScriptInterface script1("Test", "Test", g_ScriptRuntime);
|
||||
ScriptInterface script2("Test", "Test", g_ScriptRuntime);
|
||||
|
||||
CScriptVal obj1;
|
||||
TS_ASSERT(script1.Eval("({'x': 123, 'y': [1, 1.5, '2', 'test', undefined, null, true, false]})", obj1));
|
||||
JSContext* cx1 = script1.GetContext();
|
||||
JSAutoRequest rq1(cx1);
|
||||
JS::RootedValue obj1(cx1);
|
||||
TS_ASSERT(script1.Eval("({'x': 123, 'y': [1, 1.5, '2', 'test', undefined, null, true, false]})", &obj1));
|
||||
|
||||
CScriptVal obj2 = script2.CloneValueFromOtherContext(script1, obj1.get());
|
||||
{
|
||||
JSContext* cx2 = script2.GetContext();
|
||||
JSAutoRequest rq2(cx2);
|
||||
|
||||
JS::RootedValue obj2(cx2, script2.CloneValueFromOtherContext(script1, obj1));
|
||||
|
||||
std::string source;
|
||||
TS_ASSERT(script2.CallFunction(obj2.get(), "toSource", source));
|
||||
TS_ASSERT_STR_EQUALS(source, "({x:123, y:[1, 1.5, \"2\", \"test\", (void 0), null, true, false]})");
|
||||
std::string source;
|
||||
TS_ASSERT(script2.CallFunction(obj2, "toSource", source));
|
||||
TS_ASSERT_STR_EQUALS(source, "({x:123, y:[1, 1.5, \"2\", \"test\", (void 0), null, true, false]})");
|
||||
}
|
||||
}
|
||||
|
||||
void test_clone_getters()
|
||||
@@ -81,14 +88,22 @@ public:
|
||||
ScriptInterface script1("Test", "Test", g_ScriptRuntime);
|
||||
ScriptInterface script2("Test", "Test", g_ScriptRuntime);
|
||||
|
||||
CScriptVal obj1;
|
||||
TS_ASSERT(script1.Eval("var s = '?'; var v = ({get x() { return 123 }, 'y': {'w':{get z() { delete v.y; delete v.n; v = null; s += s; return 4 }}}, 'n': 100}); v", obj1));
|
||||
JSContext* cx1 = script1.GetContext();
|
||||
JSAutoRequest rq1(cx1);
|
||||
|
||||
JS::RootedValue obj1(cx1);
|
||||
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));
|
||||
|
||||
CScriptVal obj2 = script2.CloneValueFromOtherContext(script1, obj1.get());
|
||||
{
|
||||
JSContext* cx2 = script2.GetContext();
|
||||
JSAutoRequest rq2(cx2);
|
||||
|
||||
JS::RootedValue obj2(cx2, script2.CloneValueFromOtherContext(script1, obj1));
|
||||
|
||||
std::string source;
|
||||
TS_ASSERT(script2.CallFunction(obj2.get(), "toSource", source));
|
||||
TS_ASSERT_STR_EQUALS(source, "({x:123, y:{w:{z:4}}})");
|
||||
std::string source;
|
||||
TS_ASSERT(script2.CallFunction(obj2, "toSource", source));
|
||||
TS_ASSERT_STR_EQUALS(source, "({x:123, y:{w:{z:4}}})");
|
||||
}
|
||||
}
|
||||
|
||||
void test_clone_cyclic()
|
||||
@@ -96,24 +111,29 @@ public:
|
||||
ScriptInterface script1("Test", "Test", g_ScriptRuntime);
|
||||
ScriptInterface script2("Test", "Test", g_ScriptRuntime);
|
||||
|
||||
CScriptVal obj1;
|
||||
TS_ASSERT(script1.Eval("var x = []; x[0] = x; ({'a': x, 'b': x})", obj1));
|
||||
JSContext* cx1 = script1.GetContext();
|
||||
JSAutoRequest rq1(cx1);
|
||||
|
||||
JS::RootedValue obj1(cx1);
|
||||
TS_ASSERT(script1.Eval("var x = []; x[0] = x; ({'a': x, 'b': x})", &obj1));
|
||||
|
||||
CScriptVal obj2 = script2.CloneValueFromOtherContext(script1, obj1.get());
|
||||
{
|
||||
JSContext* cx2 = script2.GetContext();
|
||||
JSAutoRequest rq(cx2);
|
||||
JS::RootedValue obj2(cx2, script2.CloneValueFromOtherContext(script1, obj1));
|
||||
|
||||
// Use JSAPI function to check if the values of the properties "a", "b" are equals a.x[0]
|
||||
JSContext* cx2 = script2.GetContext();
|
||||
JSAutoRequest rq(cx2);
|
||||
JS::RootedValue prop_a(cx2);
|
||||
JS::RootedValue prop_b(cx2);
|
||||
JS::RootedValue prop_x1(cx2);
|
||||
TS_ASSERT(JS_GetProperty(cx2, &(obj2.get().toObject()), "a", prop_a.address()));
|
||||
TS_ASSERT(JS_GetProperty(cx2, &(obj2.get().toObject()), "b", prop_b.address()));
|
||||
TS_ASSERT(prop_a.get().isObject());
|
||||
TS_ASSERT(prop_b.get().isObject());
|
||||
TS_ASSERT(JS_GetProperty(cx2, &(prop_a.get().toObject()), "0", prop_x1.address()));
|
||||
TS_ASSERT_EQUALS(prop_x1.get(), prop_a.get());
|
||||
TS_ASSERT_EQUALS(prop_x1.get(), prop_b.get());
|
||||
// Use JSAPI function to check if the values of the properties "a", "b" are equals a.x[0]
|
||||
JS::RootedValue prop_a(cx2);
|
||||
JS::RootedValue prop_b(cx2);
|
||||
JS::RootedValue prop_x1(cx2);
|
||||
TS_ASSERT(script2.GetProperty(obj2, "a", &prop_a));
|
||||
TS_ASSERT(script2.GetProperty(obj2, "b", &prop_b));
|
||||
TS_ASSERT(prop_a.isObject());
|
||||
TS_ASSERT(prop_b.isObject());
|
||||
TS_ASSERT(script2.GetProperty(prop_a, "0", &prop_x1));
|
||||
TS_ASSERT_EQUALS(prop_x1.get(), prop_a.get());
|
||||
TS_ASSERT_EQUALS(prop_x1.get(), prop_b.get());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user