Move CEF V8 internal attributes to hidden values and add a test to verify safety (issue #316).

git-svn-id: https://chromiumembedded.googlecode.com/svn/trunk@361 5089003a-bbd8-11dd-ad1f-f1f9622dbc98
This commit is contained in:
Marshall Greenblatt 2011-11-07 21:39:57 +00:00
parent 53a2f21ba4
commit ae5371dd09
5 changed files with 510 additions and 46 deletions

View File

@ -2508,7 +2508,6 @@ public:
// OBJECT METHODS - These methods are only available on objects. Arrays and
// functions are also objects. String- and integer-based keys can be used
// interchangably with the framework converting between them as necessary.
// Keys beginning with "Cef::" and "v8::" are reserved by the system.
///
// Returns true if the object has a value with the specified identifier.

View File

@ -2226,7 +2226,6 @@ typedef struct _cef_v8value_t
// OBJECT METHODS - These functions are only available on objects. Arrays and
// functions are also objects. String- and integer-based keys can be used
// interchangably with the framework converting between them as necessary.
// Keys beginning with "Cef::" and "v8::" are reserved by the system.
///
// Returns true (1) if the object has a value with the specified identifier.

View File

@ -13,13 +13,13 @@
#define CEF_REQUIRE_UI_THREAD(var) \
if (!CefThread::CurrentlyOn(CefThread::UI)) { \
NOTREACHED(); \
NOTREACHED() << "called on invalid thread"; \
return var; \
}
#define CEF_REQUIRE_VALID_CONTEXT(var) \
if (!CONTEXT_STATE_VALID()) { \
NOTREACHED(); \
NOTREACHED() << "context not valid"; \
return var; \
}
@ -29,9 +29,6 @@ namespace {
static const char kCefAccessor[] = "Cef::Accessor";
static const char kCefHandler[] = "Cef::Handler";
static const char kCefUserData[] = "Cef::UserData";
static const v8::PropertyAttribute kInternalAttributes =
static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum |
v8::DontDelete);
// Memory manager.
@ -209,14 +206,8 @@ v8::Handle<v8::Value> AccessorGetterCallbackImpl(v8::Local<v8::String> property,
v8::HandleScope handle_scope;
v8::Handle<v8::Object> obj = info.This();
v8::Handle<v8::String> key = v8::String::New(kCefAccessor);
CefV8Accessor* accessorPtr = NULL;
if (obj->Has(key)) {
accessorPtr = static_cast<CefV8Accessor*>(v8::External::Unwrap(
obj->Get(key)));
}
CefV8Accessor* accessorPtr = CefV8ValueImpl::GetAccessor(obj);
if (accessorPtr) {
CefRefPtr<CefV8Value> retval;
CefRefPtr<CefV8Value> object = new CefV8ValueImpl(obj);
@ -244,14 +235,8 @@ void AccessorSetterCallbackImpl(v8::Local<v8::String> property,
v8::HandleScope handle_scope;
v8::Handle<v8::Object> obj = info.This();
v8::Handle<v8::String> key = v8::String::New(kCefAccessor);
CefV8Accessor* accessorPtr = NULL;
if (obj->Has(key)) {
accessorPtr = static_cast<CefV8Accessor*>(v8::External::Unwrap(
obj->Get(key)));
}
CefV8Accessor* accessorPtr = CefV8ValueImpl::GetAccessor(obj);
if (accessorPtr) {
CefRefPtr<CefV8Value> object = new CefV8ValueImpl(obj);
CefRefPtr<CefV8Value> cefValue = new CefV8ValueImpl(value);
@ -554,13 +539,13 @@ CefRefPtr<CefV8Value> CefV8Value::CreateObject(
// Attach the user data to the V8 object.
if (user_data.get()) {
v8::Local<v8::Value> data = v8::External::Wrap(user_data.get());
obj->Set(v8::String::New(kCefUserData), data, kInternalAttributes);
obj->SetHiddenValue(v8::String::New(kCefUserData), data);
}
// Attach the accessor to the V8 object.
if (accessor.get()) {
v8::Local<v8::Value> data = v8::External::Wrap(accessor.get());
obj->Set(v8::String::New(kCefAccessor), data, kInternalAttributes);
obj->SetHiddenValue(v8::String::New(kCefAccessor), data);
}
return new CefV8ValueImpl(obj, tracker);
@ -578,16 +563,21 @@ CefRefPtr<CefV8Value> CefV8Value::CreateArray()
// static
CefRefPtr<CefV8Value> CefV8Value::CreateFunction(const CefString& name,
CefRefPtr<CefV8Handler> handler)
CefRefPtr<CefV8Handler> handler)
{
CEF_REQUIRE_VALID_CONTEXT(NULL);
CEF_REQUIRE_UI_THREAD(NULL);
if (!handler.get()) {
NOTREACHED() << "invalid parameter";
return NULL;
}
v8::HandleScope handle_scope;
// Create a new V8 function template with one internal field.
v8::Local<v8::FunctionTemplate> tmpl = v8::FunctionTemplate::New();
v8::Local<v8::Value> data = v8::External::Wrap(handler.get());
// Set the function handler callback.
@ -598,7 +588,7 @@ CefRefPtr<CefV8Value> CefV8Value::CreateFunction(const CefString& name,
func->SetName(GetV8String(name));
// Attach the handler instance to the V8 object.
func->Set(v8::String::New(kCefHandler), data, kInternalAttributes);
func->SetHiddenValue(v8::String::New(kCefHandler), data);
// Create the CefV8ValueImpl and provide a tracker object that will cause
// the handler reference to be released when the V8 object is destroyed.
@ -746,8 +736,6 @@ CefString CefV8ValueImpl::GetStringValue()
bool CefV8ValueImpl::HasValue(const CefString& key)
{
CEF_REQUIRE_UI_THREAD(false);
if(IsReservedKey(key))
return false;
if(!GetHandle()->IsObject()) {
NOTREACHED();
return false;
@ -774,8 +762,6 @@ bool CefV8ValueImpl::HasValue(int index)
bool CefV8ValueImpl::DeleteValue(const CefString& key)
{
CEF_REQUIRE_UI_THREAD(false);
if(IsReservedKey(key))
return false;
if(!GetHandle()->IsObject()) {
NOTREACHED();
return false;
@ -802,8 +788,6 @@ bool CefV8ValueImpl::DeleteValue(int index)
CefRefPtr<CefV8Value> CefV8ValueImpl::GetValue(const CefString& key)
{
CEF_REQUIRE_UI_THREAD(NULL);
if(IsReservedKey(key))
return NULL;
if(!GetHandle()->IsObject()) {
NOTREACHED();
return NULL;
@ -832,8 +816,6 @@ bool CefV8ValueImpl::SetValue(const CefString& key,
PropertyAttribute attribute)
{
CEF_REQUIRE_UI_THREAD(false);
if(IsReservedKey(key))
return false;
if(!GetHandle()->IsObject()) {
NOTREACHED();
return false;
@ -886,6 +868,10 @@ bool CefV8ValueImpl::SetValue(const CefString& key, AccessControl settings,
v8::HandleScope handle_scope;
v8::Local<v8::Object> obj = GetHandle()->ToObject();
// Verify that an accessor exists for this object.
if (!GetAccessor(obj))
return false;
v8::AccessorGetter getter = AccessorGetterCallbackImpl;
v8::AccessorSetter setter = (attribute & V8_PROPERTY_ATTRIBUTE_READONLY) ?
NULL : AccessorSetterCallbackImpl;
@ -912,8 +898,7 @@ bool CefV8ValueImpl::GetKeys(std::vector<CefString>& keys)
v8::Local<v8::Value> value = arr_keys->Get(v8::Integer::New(i));
CefString str;
GetCefString(value->ToString(), str);
if(!IsReservedKey(str))
keys.push_back(str);
keys.push_back(str);
}
return true;
}
@ -928,9 +913,12 @@ CefRefPtr<CefBase> CefV8ValueImpl::GetUserData()
v8::HandleScope handle_scope;
v8::Local<v8::Object> obj = GetHandle()->ToObject();
v8::Local<v8::String> key = v8::String::New(kCefUserData);
if(obj->Has(key))
return static_cast<CefBase*>(v8::External::Unwrap(obj->Get(key)));
v8::Local<v8::Value> value =
obj->GetHiddenValue(v8::String::New(kCefUserData));
if (!value.IsEmpty())
return static_cast<CefBase*>(v8::External::Unwrap(value));
return NULL;
}
@ -974,9 +962,12 @@ CefRefPtr<CefV8Handler> CefV8ValueImpl::GetFunctionHandler()
v8::HandleScope handle_scope;
v8::Local<v8::Object> obj = GetHandle()->ToObject();
v8::Local<v8::String> key = v8::String::New(kCefHandler);
if (obj->Has(key))
return static_cast<CefV8Handler*>(v8::External::Unwrap(obj->Get(key)));
v8::Local<v8::Value> value =
obj->GetHiddenValue(v8::String::New(kCefHandler));
if (!value.IsEmpty())
return static_cast<CefV8Handler*>(v8::External::Unwrap(value));
return NULL;
}
@ -1052,8 +1043,13 @@ bool CefV8ValueImpl::ExecuteFunctionWithContext(
return true;
}
bool CefV8ValueImpl::IsReservedKey(const CefString& key)
// static
CefV8Accessor* CefV8ValueImpl::GetAccessor(v8::Handle<v8::Object> object)
{
std::string str = key;
return (str.find("Cef::") == 0 || str.find("v8::") == 0);
v8::Local<v8::Value> value =
object->GetHiddenValue(v8::String::New(kCefAccessor));
if (!value.IsEmpty())
return static_cast<CefV8Accessor*>(v8::External::Unwrap(value));
return NULL;
}

View File

@ -158,7 +158,8 @@ public:
return v8_value_->GetHandle();
}
bool IsReservedKey(const CefString& key);
// Returns the accessor assigned for the specified object, if any.
static CefV8Accessor* GetAccessor(v8::Handle<v8::Object> object);
protected:
scoped_refptr<CefV8ValueHandle> v8_value_;

View File

@ -935,3 +935,472 @@ TEST(V8Test, Context)
EXPECT_TRUE(handler->got_navigation_);
EXPECT_TRUE(handler->got_testcomplete_);
}
namespace {
class TestInternalHandler : public TestHandler
{
public:
class UserData : public CefBase
{
public:
UserData(CefRefPtr<TestInternalHandler> test)
: test_(test)
{
}
void Test(const std::string& name)
{
if (name == "obj1-before") {
if (test_->nav_ == 0)
test_->got_userdata_obj1_before_test1_fail_.yes();
else
test_->got_userdata_obj1_before_test2_fail_.yes();
} else if (name == "obj2-before") {
if (test_->nav_ == 0)
test_->got_userdata_obj2_before_test1_.yes();
else
test_->got_userdata_obj2_before_test2_fail_.yes();
}else if (name == "obj1-after") {
if (test_->nav_ == 0)
test_->got_userdata_obj1_after_test1_fail_.yes();
else
test_->got_userdata_obj1_after_test2_fail_.yes();
} else if (name == "obj2-after") {
if (test_->nav_ == 0)
test_->got_userdata_obj2_after_test1_.yes();
else
test_->got_userdata_obj2_after_test2_fail_.yes();
}
}
CefRefPtr<TestInternalHandler> test_;
IMPLEMENT_REFCOUNTING(UserData);
};
class Accessor : public CefV8Accessor
{
public:
Accessor(CefRefPtr<TestInternalHandler> test)
: test_(test)
{
}
virtual bool Get(const CefString& name,
const CefRefPtr<CefV8Value> object,
CefRefPtr<CefV8Value>& retval,
CefString& exception) OVERRIDE
{
if (test_->nav_ == 0)
test_->got_accessor_get1_.yes();
else
test_->got_accessor_get2_fail_.yes();
retval = CefV8Value::CreateString("default2");
return true;
}
virtual bool Set(const CefString& name,
const CefRefPtr<CefV8Value> object,
const CefRefPtr<CefV8Value> value,
CefString& exception) OVERRIDE
{
if (test_->nav_ == 0)
test_->got_accessor_set1_.yes();
else
test_->got_accessor_set2_fail_.yes();
return true;
}
CefRefPtr<TestInternalHandler> test_;
IMPLEMENT_REFCOUNTING(Accessor);
};
class Handler : public CefV8Handler
{
public:
Handler(CefRefPtr<TestInternalHandler> test)
: test_(test),
execute_ct_(0)
{
}
virtual bool Execute(const CefString& name,
CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval,
CefString& exception) OVERRIDE
{
CefRefPtr<CefV8Handler> handler =
object->GetValue("func")->GetFunctionHandler();
if (execute_ct_ == 0) {
if (handler.get() == this)
test_->got_execute1_.yes();
else
test_->got_execute1_fail_.yes();
} else {
if (handler.get() == this)
test_->got_execute2_.yes();
else
test_->got_execute2_fail_.yes();
}
execute_ct_++;
return true;
}
CefRefPtr<TestInternalHandler> test_;
int execute_ct_;
IMPLEMENT_REFCOUNTING(Accessor);
};
class TestHandler : public CefV8Handler
{
public:
TestHandler(CefRefPtr<TestInternalHandler> test)
: test_(test)
{
}
virtual bool Execute(const CefString& name,
CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval,
CefString& exception) OVERRIDE
{
if (name == "store") {
// Store a JSON value.
if (arguments.size() == 2 && arguments[0]->IsString() &&
arguments[1]->IsString()) {
std::string name = arguments[0]->GetStringValue();
std::string val = arguments[1]->GetStringValue();
if (name == "obj1") {
test_->obj1_json_ = val;
if (val == "{\"value\":\"testval1\",\"value2\":\"default1\"}")
test_->got_obj1_json_.yes();
} else if (name == "obj2") {
test_->obj2_json_ = val;
if (val == "{\"value\":\"testval2\",\"value2\":\"default2\"}")
test_->got_obj2_json_.yes();
} else {
return false;
}
retval = CefV8Value::CreateBool(true);
return true;
}
} else if (name == "retrieve") {
// Retrieve a JSON value.
if (arguments.size() == 1 && arguments[0]->IsString()) {
std::string name = arguments[0]->GetStringValue();
std::string val;
if (name == "obj1")
val = test_->obj1_json_;
else if (name == "obj2")
val = test_->obj2_json_;
if (!val.empty()) {
retval = CefV8Value::CreateString(val);
return true;
}
}
} else if (name == "userdata") {
if (arguments.size() == 2 && arguments[0]->IsString() &&
arguments[1]->IsObject()) {
std::string name = arguments[0]->GetStringValue();
CefRefPtr<UserData> userData =
reinterpret_cast<UserData*>(arguments[1]->GetUserData().get());
if (!userData.get()) {
// No UserData object.
if (name == "obj1-before") {
if (test_->nav_ == 0)
test_->got_userdata_obj1_before_null1_.yes();
else
test_->got_userdata_obj1_before_null2_.yes();
} else if (name == "obj2-before") {
if (test_->nav_ == 0)
test_->got_userdata_obj2_before_null1_fail_.yes();
else
test_->got_userdata_obj2_before_null2_.yes();
} else if (name == "obj1-after") {
if (test_->nav_ == 0)
test_->got_userdata_obj1_after_null1_.yes();
else
test_->got_userdata_obj1_after_null2_.yes();
} else if (name == "obj2-after") {
if (test_->nav_ == 0)
test_->got_userdata_obj2_after_null1_fail_.yes();
else
test_->got_userdata_obj2_after_null2_.yes();
}
} else {
// Call the test function.
userData->Test(name);
}
return true;
}
} else if (name == "record") {
if (arguments.size() == 1 && arguments[0]->IsString()) {
std::string name = arguments[0]->GetStringValue();
if (name == "userdata-obj1-set-succeed") {
if (test_->nav_ == 0)
test_->got_userdata_obj1_set_succeed1_.yes();
else
test_->got_userdata_obj1_set_succeed2_.yes();
} else if (name == "userdata-obj1-set-except") {
if (test_->nav_ == 0)
test_->got_userdata_obj1_set_except1_fail_.yes();
else
test_->got_userdata_obj1_set_except2_fail_.yes();
} else if (name == "userdata-obj2-set-succeed") {
if (test_->nav_ == 0)
test_->got_userdata_obj2_set_succeed1_.yes();
else
test_->got_userdata_obj2_set_succeed2_.yes();
} else if (name == "userdata-obj2-set-except") {
if (test_->nav_ == 0)
test_->got_userdata_obj2_set_except1_fail_.yes();
else
test_->got_userdata_obj2_set_except2_fail_.yes();
} else if (name == "func-set-succeed") {
test_->got_func_set_succeed_.yes();
} else if (name == "func-set-except") {
test_->got_func_set_except_fail_.yes();
}
return true;
}
}
return false;
}
CefRefPtr<TestInternalHandler> test_;
IMPLEMENT_REFCOUNTING(Handler);
};
TestInternalHandler()
: nav_(0)
{
}
virtual void RunTest() OVERRIDE
{
std::string tests =
// Test userdata retrieval.
"window.test.userdata('obj1-before', window.obj1);\n"
"window.test.userdata('obj2-before', window.obj2);\n"
// Test accessors.
"window.obj1.value2 = 'newval1';\n"
"window.obj2.value2 = 'newval2';\n"
"val1 = window.obj1.value2;\n"
"val2 = window.obj2.value2;\n"
// Test setting the hidden internal values.
"try { window.obj1['Cef::UserData'] = 1;\n"
"window.obj1['Cef::Accessor'] = 1;\n"
"window.test.record('userdata-obj1-set-succeed'); }\n"
"catch(e) { window.test.record('userdata-obj1-set-except'); }\n"
"try { window.obj2['Cef::UserData'] = 1;\n"
"window.obj2['Cef::Accessor'] = 1;\n"
"window.test.record('userdata-obj2-set-succeed'); }\n"
"catch(e) { window.test.record('userdata-obj2-set-except'); }\n"
// Test userdata retrieval after messing with the internal values.
"window.test.userdata('obj1-after', window.obj1);\n"
"window.test.userdata('obj2-after', window.obj2);\n"
// Test accessors after messing with the internal values.
"window.obj1.value2 = 'newval1';\n"
"window.obj2.value2 = 'newval2';\n"
"val1 = window.obj1.value2;\n"
"val2 = window.obj2.value2;\n";
std::stringstream testHtml;
testHtml <<
"<html><body>\n"
"<script language=\"JavaScript\">\n"
// Serialize the bound values.
"window.test.store('obj1', JSON.stringify(window.obj1));\n"
"window.test.store('obj2', JSON.stringify(window.obj2));\n"
// Run the tests.
<< tests.c_str() <<
// Test function call.
"window.func();\n"
// Test setting the hidden internal values.
"try { window.func['Cef::Handler'] = 1;\n"
"window.test.record('func-set-succeed'); }\n"
"catch(e) { window.test.record('func-set-except'); }\n"
// Test function call after messing with internal values.
"window.func();\n"
"</script>\n"
"</body></html>";
AddResource("http://tests/run1.html", testHtml.str(), "text/html");
testHtml.str("");
testHtml <<
"<html><body>\n"
"<script language=\"JavaScript\">\n"
// Deserialize the bound values.
"window.obj1 = JSON.parse(window.test.retrieve('obj1'));\n"
"window.obj2 = JSON.parse(window.test.retrieve('obj2'));\n"
// Run the tests.
<< tests.c_str() <<
"</script>\n"
"</body></html>";
AddResource("http://tests/run2.html", testHtml.str(), "text/html");
testHtml.str("");
CreateBrowser("http://tests/run1.html");
}
virtual void OnLoadEnd(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
int httpStatusCode) OVERRIDE
{
if (nav_ == 0) {
// Navigate to the next page.
frame->LoadURL("http://tests/run2.html");
} else {
DestroyTest();
}
nav_++;
}
virtual void OnJSBinding(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Value> object) OVERRIDE
{
if (nav_ == 0) {
// Create an object without any internal values.
CefRefPtr<CefV8Value> obj1 = CefV8Value::CreateObject(NULL, NULL);
obj1->SetValue("value", CefV8Value::CreateString("testval1"),
V8_PROPERTY_ATTRIBUTE_NONE);
obj1->SetValue("value2", CefV8Value::CreateString("default1"),
V8_PROPERTY_ATTRIBUTE_NONE);
object->SetValue("obj1", obj1, V8_PROPERTY_ATTRIBUTE_NONE);
// Create an object with Cef::Accessor and Cef::UserData internal values.
CefRefPtr<CefV8Value> obj2 =
CefV8Value::CreateObject(new UserData(this), new Accessor(this));
obj2->SetValue("value", CefV8Value::CreateString("testval2"),
V8_PROPERTY_ATTRIBUTE_NONE);
obj2->SetValue("value2", V8_ACCESS_CONTROL_DEFAULT,
V8_PROPERTY_ATTRIBUTE_NONE);
object->SetValue("obj2", obj2, V8_PROPERTY_ATTRIBUTE_NONE);
// Create a function with Cef::Handler internal value.
CefRefPtr<CefV8Value> func =
CefV8Value::CreateFunction("func", new Handler(this));
object->SetValue("func", func, V8_PROPERTY_ATTRIBUTE_NONE);
}
// Used for executing the test.
CefRefPtr<CefV8Handler> handler = new TestHandler(this);
CefRefPtr<CefV8Value> obj = CefV8Value::CreateObject(NULL, NULL);
obj->SetValue("store", CefV8Value::CreateFunction("store", handler),
V8_PROPERTY_ATTRIBUTE_NONE);
obj->SetValue("retrieve", CefV8Value::CreateFunction("retrieve", handler),
V8_PROPERTY_ATTRIBUTE_NONE);
obj->SetValue("userdata", CefV8Value::CreateFunction("userdata", handler),
V8_PROPERTY_ATTRIBUTE_NONE);
obj->SetValue("record", CefV8Value::CreateFunction("record", handler),
V8_PROPERTY_ATTRIBUTE_NONE);
object->SetValue("test", obj, V8_PROPERTY_ATTRIBUTE_NONE);
}
int nav_;
std::string obj1_json_, obj2_json_;
TrackCallback got_obj1_json_;
TrackCallback got_obj2_json_;
TrackCallback got_userdata_obj1_before_null1_;
TrackCallback got_userdata_obj2_before_null1_fail_;
TrackCallback got_userdata_obj1_before_test1_fail_;
TrackCallback got_userdata_obj2_before_test1_;
TrackCallback got_userdata_obj1_set_succeed1_;
TrackCallback got_userdata_obj1_set_except1_fail_;
TrackCallback got_userdata_obj2_set_succeed1_;
TrackCallback got_userdata_obj2_set_except1_fail_;
TrackCallback got_userdata_obj1_after_null1_;
TrackCallback got_userdata_obj2_after_null1_fail_;
TrackCallback got_userdata_obj1_after_test1_fail_;
TrackCallback got_userdata_obj2_after_test1_;
TrackCallback got_userdata_obj1_before_null2_;
TrackCallback got_userdata_obj2_before_null2_;
TrackCallback got_userdata_obj1_before_test2_fail_;
TrackCallback got_userdata_obj2_before_test2_fail_;
TrackCallback got_userdata_obj1_set_succeed2_;
TrackCallback got_userdata_obj1_set_except2_fail_;
TrackCallback got_userdata_obj2_set_succeed2_;
TrackCallback got_userdata_obj2_set_except2_fail_;
TrackCallback got_userdata_obj1_after_null2_;
TrackCallback got_userdata_obj2_after_null2_;
TrackCallback got_userdata_obj1_after_test2_fail_;
TrackCallback got_userdata_obj2_after_test2_fail_;
TrackCallback got_accessor_get1_;
TrackCallback got_accessor_get2_fail_;
TrackCallback got_accessor_set1_;
TrackCallback got_accessor_set2_fail_;
TrackCallback got_execute1_;
TrackCallback got_execute1_fail_;
TrackCallback got_func_set_succeed_;
TrackCallback got_func_set_except_fail_;
TrackCallback got_execute2_;
TrackCallback got_execute2_fail_;
};
} // namespace
// Test that messing around with CEF internal values doesn't cause crashes.
TEST(V8Test, Internal)
{
CefRefPtr<TestInternalHandler> handler = new TestInternalHandler();
handler->ExecuteTest();
EXPECT_TRUE(handler->got_obj1_json_.isSet());
EXPECT_TRUE(handler->got_obj2_json_.isSet());
EXPECT_TRUE(handler->got_userdata_obj1_before_null1_.isSet());
EXPECT_FALSE(handler->got_userdata_obj2_before_null1_fail_.isSet());
EXPECT_FALSE(handler->got_userdata_obj1_before_test1_fail_.isSet());
EXPECT_TRUE(handler->got_userdata_obj2_before_test1_.isSet());
EXPECT_TRUE(handler->got_userdata_obj1_set_succeed1_.isSet());
EXPECT_FALSE(handler->got_userdata_obj1_set_except1_fail_.isSet());
EXPECT_TRUE(handler->got_userdata_obj2_set_succeed1_.isSet());
EXPECT_FALSE(handler->got_userdata_obj2_set_except1_fail_.isSet());
EXPECT_TRUE(handler->got_userdata_obj1_after_null1_.isSet());
EXPECT_FALSE(handler->got_userdata_obj2_after_null1_fail_.isSet());
EXPECT_FALSE(handler->got_userdata_obj1_after_test1_fail_.isSet());
EXPECT_TRUE(handler->got_userdata_obj2_after_test1_.isSet());
EXPECT_TRUE(handler->got_userdata_obj1_before_null2_.isSet());
EXPECT_TRUE(handler->got_userdata_obj2_before_null2_.isSet());
EXPECT_FALSE(handler->got_userdata_obj1_before_test2_fail_.isSet());
EXPECT_FALSE(handler->got_userdata_obj2_before_test2_fail_.isSet());
EXPECT_TRUE(handler->got_userdata_obj1_set_succeed2_.isSet());
EXPECT_FALSE(handler->got_userdata_obj1_set_except2_fail_.isSet());
EXPECT_TRUE(handler->got_userdata_obj2_set_succeed2_.isSet());
EXPECT_FALSE(handler->got_userdata_obj2_set_except2_fail_.isSet());
EXPECT_TRUE(handler->got_userdata_obj1_after_null2_.isSet());
EXPECT_TRUE(handler->got_userdata_obj2_after_null2_.isSet());
EXPECT_FALSE(handler->got_userdata_obj1_after_test2_fail_.isSet());
EXPECT_FALSE(handler->got_userdata_obj2_after_test2_fail_.isSet());
EXPECT_TRUE(handler->got_accessor_get1_.isSet());
EXPECT_FALSE(handler->got_accessor_get2_fail_.isSet());
EXPECT_TRUE(handler->got_accessor_set1_.isSet());
EXPECT_FALSE(handler->got_accessor_set2_fail_.isSet());
EXPECT_TRUE(handler->got_execute1_.isSet());
EXPECT_FALSE(handler->got_execute1_fail_.isSet());
EXPECT_TRUE(handler->got_execute2_.isSet());
EXPECT_FALSE(handler->got_execute2_fail_.isSet());
}