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:
Yves
2014-07-31 19:18:40 +00:00
parent d677a30c39
commit 5c07a25ddc
26 changed files with 320 additions and 237 deletions
+3 -3
View File
@@ -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
+6 -9
View File
@@ -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; \
+7 -6
View File
@@ -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;
}
+10 -13
View File
@@ -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());
}
}
/**