Add code that reads the APPLE_property debug info, and makes up properties from them.
llvm-svn: 144440
This commit is contained in:
parent
3a3d8e82bc
commit
e3ae82af89
|
@ -80,6 +80,7 @@ namespace clang
|
||||||
class ObjCEncodeExpr;
|
class ObjCEncodeExpr;
|
||||||
class ObjCImplicitSetterGetterRefExpr;
|
class ObjCImplicitSetterGetterRefExpr;
|
||||||
class ObjCInterfaceDecl;
|
class ObjCInterfaceDecl;
|
||||||
|
class ObjCIvarDecl;
|
||||||
class ObjCIvarRefExpr;
|
class ObjCIvarRefExpr;
|
||||||
class ObjCMessageExpr;
|
class ObjCMessageExpr;
|
||||||
class ObjCMethodDecl;
|
class ObjCMethodDecl;
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "llvm/ADT/SmallVector.h"
|
#include "llvm/ADT/SmallVector.h"
|
||||||
#include "clang/AST/TemplateBase.h"
|
#include "clang/AST/TemplateBase.h"
|
||||||
|
|
||||||
|
|
||||||
// Project includes
|
// Project includes
|
||||||
#include "lldb/lldb-enumerations.h"
|
#include "lldb/lldb-enumerations.h"
|
||||||
#include "lldb/Core/ClangForward.h"
|
#include "lldb/Core/ClangForward.h"
|
||||||
|
@ -247,7 +248,7 @@ public:
|
||||||
int kind,
|
int kind,
|
||||||
lldb::LanguageType language);
|
lldb::LanguageType language);
|
||||||
|
|
||||||
static bool
|
static clang::FieldDecl *
|
||||||
AddFieldToRecordType (clang::ASTContext *ast,
|
AddFieldToRecordType (clang::ASTContext *ast,
|
||||||
lldb::clang_type_t record_qual_type,
|
lldb::clang_type_t record_qual_type,
|
||||||
const char *name,
|
const char *name,
|
||||||
|
@ -255,7 +256,7 @@ public:
|
||||||
lldb::AccessType access,
|
lldb::AccessType access,
|
||||||
uint32_t bitfield_bit_size);
|
uint32_t bitfield_bit_size);
|
||||||
|
|
||||||
bool
|
clang::FieldDecl *
|
||||||
AddFieldToRecordType (lldb::clang_type_t record_qual_type,
|
AddFieldToRecordType (lldb::clang_type_t record_qual_type,
|
||||||
const char *name,
|
const char *name,
|
||||||
lldb::clang_type_t field_type,
|
lldb::clang_type_t field_type,
|
||||||
|
@ -383,7 +384,7 @@ public:
|
||||||
bool isForwardDecl,
|
bool isForwardDecl,
|
||||||
bool isInternal);
|
bool isInternal);
|
||||||
|
|
||||||
static bool
|
static clang::FieldDecl *
|
||||||
AddObjCClassIVar (clang::ASTContext *ast,
|
AddObjCClassIVar (clang::ASTContext *ast,
|
||||||
lldb::clang_type_t class_opaque_type,
|
lldb::clang_type_t class_opaque_type,
|
||||||
const char *name,
|
const char *name,
|
||||||
|
@ -392,7 +393,7 @@ public:
|
||||||
uint32_t bitfield_bit_size,
|
uint32_t bitfield_bit_size,
|
||||||
bool isSynthesized);
|
bool isSynthesized);
|
||||||
|
|
||||||
bool
|
clang::FieldDecl *
|
||||||
AddObjCClassIVar (lldb::clang_type_t class_opaque_type,
|
AddObjCClassIVar (lldb::clang_type_t class_opaque_type,
|
||||||
const char *name,
|
const char *name,
|
||||||
lldb::clang_type_t ivar_opaque_type,
|
lldb::clang_type_t ivar_opaque_type,
|
||||||
|
@ -409,6 +410,41 @@ public:
|
||||||
isSynthesized);
|
isSynthesized);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
AddObjCClassProperty
|
||||||
|
(
|
||||||
|
clang::ASTContext *ast,
|
||||||
|
lldb::clang_type_t class_opaque_type,
|
||||||
|
const char *property_name,
|
||||||
|
lldb::clang_type_t property_opaque_type, // The property type is only required if you don't have an ivar decl
|
||||||
|
clang::ObjCIvarDecl *ivar_decl,
|
||||||
|
const char *property_setter_name,
|
||||||
|
const char *property_getter_name,
|
||||||
|
uint32_t property_attributes
|
||||||
|
);
|
||||||
|
|
||||||
|
bool
|
||||||
|
AddObjCClassProperty
|
||||||
|
(
|
||||||
|
lldb::clang_type_t class_opaque_type,
|
||||||
|
const char *property_name,
|
||||||
|
lldb::clang_type_t property_opaque_type,
|
||||||
|
clang::ObjCIvarDecl *ivar_decl,
|
||||||
|
const char *property_setter_name,
|
||||||
|
const char *property_getter_name,
|
||||||
|
uint32_t property_attributes
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return ClangASTContext::AddObjCClassProperty (getASTContext(),
|
||||||
|
class_opaque_type,
|
||||||
|
property_name,
|
||||||
|
property_opaque_type,
|
||||||
|
ivar_decl,
|
||||||
|
property_setter_name,
|
||||||
|
property_getter_name,
|
||||||
|
property_attributes);
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
SetObjCSuperClass (lldb::clang_type_t class_clang_type,
|
SetObjCSuperClass (lldb::clang_type_t class_clang_type,
|
||||||
lldb::clang_type_t superclass_clang_type);
|
lldb::clang_type_t superclass_clang_type);
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "clang/AST/ASTContext.h"
|
#include "clang/AST/ASTContext.h"
|
||||||
#include "clang/AST/Decl.h"
|
#include "clang/AST/Decl.h"
|
||||||
#include "clang/AST/DeclGroup.h"
|
#include "clang/AST/DeclGroup.h"
|
||||||
|
#include "clang/AST/DeclObjC.h"
|
||||||
#include "clang/Basic/Builtins.h"
|
#include "clang/Basic/Builtins.h"
|
||||||
#include "clang/Basic/IdentifierTable.h"
|
#include "clang/Basic/IdentifierTable.h"
|
||||||
#include "clang/Basic/LangOptions.h"
|
#include "clang/Basic/LangOptions.h"
|
||||||
|
@ -1324,6 +1325,12 @@ SymbolFileDWARF::ParseChildMembers
|
||||||
Declaration decl;
|
Declaration decl;
|
||||||
//DWARFExpression location;
|
//DWARFExpression location;
|
||||||
const char *name = NULL;
|
const char *name = NULL;
|
||||||
|
const char *prop_name = NULL;
|
||||||
|
const char *prop_getter_name = NULL;
|
||||||
|
const char *prop_setter_name = NULL;
|
||||||
|
uint32_t prop_attributes = 0;
|
||||||
|
|
||||||
|
|
||||||
bool is_artificial = false;
|
bool is_artificial = false;
|
||||||
lldb::user_id_t encoding_uid = LLDB_INVALID_UID;
|
lldb::user_id_t encoding_uid = LLDB_INVALID_UID;
|
||||||
AccessType accessibility = eAccessNone;
|
AccessType accessibility = eAccessNone;
|
||||||
|
@ -1369,6 +1376,12 @@ SymbolFileDWARF::ParseChildMembers
|
||||||
case DW_AT_description:
|
case DW_AT_description:
|
||||||
case DW_AT_mutable:
|
case DW_AT_mutable:
|
||||||
case DW_AT_visibility:
|
case DW_AT_visibility:
|
||||||
|
|
||||||
|
case DW_AT_APPLE_property_name: prop_name = form_value.AsCString(&get_debug_str_data()); break;
|
||||||
|
case DW_AT_APPLE_property_getter: prop_getter_name = form_value.AsCString(&get_debug_str_data()); break;
|
||||||
|
case DW_AT_APPLE_property_setter: prop_setter_name = form_value.AsCString(&get_debug_str_data()); break;
|
||||||
|
case DW_AT_APPLE_property_attribute: prop_attributes = form_value.Unsigned(); break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
case DW_AT_sibling:
|
case DW_AT_sibling:
|
||||||
break;
|
break;
|
||||||
|
@ -1415,13 +1428,14 @@ SymbolFileDWARF::ParseChildMembers
|
||||||
if (is_artificial == false)
|
if (is_artificial == false)
|
||||||
{
|
{
|
||||||
Type *member_type = ResolveTypeUID(encoding_uid);
|
Type *member_type = ResolveTypeUID(encoding_uid);
|
||||||
|
clang::FieldDecl *field_decl = NULL;
|
||||||
if (member_type)
|
if (member_type)
|
||||||
{
|
{
|
||||||
if (accessibility == eAccessNone)
|
if (accessibility == eAccessNone)
|
||||||
accessibility = default_accessibility;
|
accessibility = default_accessibility;
|
||||||
member_accessibilities.push_back(accessibility);
|
member_accessibilities.push_back(accessibility);
|
||||||
|
|
||||||
GetClangASTContext().AddFieldToRecordType (class_clang_type,
|
field_decl = GetClangASTContext().AddFieldToRecordType (class_clang_type,
|
||||||
name,
|
name,
|
||||||
member_type->GetClangLayoutType(),
|
member_type->GetClangLayoutType(),
|
||||||
accessibility,
|
accessibility,
|
||||||
|
@ -1439,6 +1453,22 @@ SymbolFileDWARF::ParseChildMembers
|
||||||
MakeUserID(die->GetOffset()),
|
MakeUserID(die->GetOffset()),
|
||||||
encoding_uid);
|
encoding_uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (prop_name != NULL)
|
||||||
|
{
|
||||||
|
|
||||||
|
clang::ObjCIvarDecl *ivar_decl = clang::dyn_cast<clang::ObjCIvarDecl>(field_decl);
|
||||||
|
assert (ivar_decl != NULL);
|
||||||
|
|
||||||
|
|
||||||
|
GetClangASTContext().AddObjCClassProperty (class_clang_type,
|
||||||
|
prop_name,
|
||||||
|
0,
|
||||||
|
ivar_decl,
|
||||||
|
prop_setter_name,
|
||||||
|
prop_getter_name,
|
||||||
|
prop_attributes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
++member_idx;
|
++member_idx;
|
||||||
|
|
|
@ -1782,7 +1782,7 @@ ClangASTContext::AddMethodToCXXRecordType
|
||||||
return cxx_method_decl;
|
return cxx_method_decl;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
clang::FieldDecl *
|
||||||
ClangASTContext::AddFieldToRecordType
|
ClangASTContext::AddFieldToRecordType
|
||||||
(
|
(
|
||||||
ASTContext *ast,
|
ASTContext *ast,
|
||||||
|
@ -1794,8 +1794,9 @@ ClangASTContext::AddFieldToRecordType
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (record_clang_type == NULL || field_type == NULL)
|
if (record_clang_type == NULL || field_type == NULL)
|
||||||
return false;
|
return NULL;
|
||||||
|
|
||||||
|
FieldDecl *field = NULL;
|
||||||
IdentifierTable *identifier_table = &ast->Idents;
|
IdentifierTable *identifier_table = &ast->Idents;
|
||||||
|
|
||||||
assert (ast != NULL);
|
assert (ast != NULL);
|
||||||
|
@ -1818,7 +1819,7 @@ ClangASTContext::AddFieldToRecordType
|
||||||
APInt bitfield_bit_size_apint(ast->getTypeSize(ast->IntTy), bitfield_bit_size);
|
APInt bitfield_bit_size_apint(ast->getTypeSize(ast->IntTy), bitfield_bit_size);
|
||||||
bit_width = new (*ast)IntegerLiteral (*ast, bitfield_bit_size_apint, ast->IntTy, SourceLocation());
|
bit_width = new (*ast)IntegerLiteral (*ast, bitfield_bit_size_apint, ast->IntTy, SourceLocation());
|
||||||
}
|
}
|
||||||
FieldDecl *field = FieldDecl::Create (*ast,
|
field = FieldDecl::Create (*ast,
|
||||||
record_decl,
|
record_decl,
|
||||||
SourceLocation(),
|
SourceLocation(),
|
||||||
SourceLocation(),
|
SourceLocation(),
|
||||||
|
@ -1846,7 +1847,7 @@ ClangASTContext::AddFieldToRecordType
|
||||||
if (objc_class_type)
|
if (objc_class_type)
|
||||||
{
|
{
|
||||||
bool is_synthesized = false;
|
bool is_synthesized = false;
|
||||||
ClangASTContext::AddObjCClassIVar (ast,
|
field = ClangASTContext::AddObjCClassIVar (ast,
|
||||||
record_clang_type,
|
record_clang_type,
|
||||||
name,
|
name,
|
||||||
field_type,
|
field_type,
|
||||||
|
@ -1856,7 +1857,7 @@ ClangASTContext::AddFieldToRecordType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return field;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -2047,7 +2048,7 @@ ClangASTContext::SetObjCSuperClass (clang_type_t class_opaque_type, clang_type_t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool
|
FieldDecl *
|
||||||
ClangASTContext::AddObjCClassIVar
|
ClangASTContext::AddObjCClassIVar
|
||||||
(
|
(
|
||||||
ASTContext *ast,
|
ASTContext *ast,
|
||||||
|
@ -2060,8 +2061,10 @@ ClangASTContext::AddObjCClassIVar
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (class_opaque_type == NULL || ivar_opaque_type == NULL)
|
if (class_opaque_type == NULL || ivar_opaque_type == NULL)
|
||||||
return false;
|
return NULL;
|
||||||
|
|
||||||
|
ObjCIvarDecl *field = NULL;
|
||||||
|
|
||||||
IdentifierTable *identifier_table = &ast->Idents;
|
IdentifierTable *identifier_table = &ast->Idents;
|
||||||
|
|
||||||
assert (ast != NULL);
|
assert (ast != NULL);
|
||||||
|
@ -2087,16 +2090,16 @@ ClangASTContext::AddObjCClassIVar
|
||||||
bit_width = new (*ast)IntegerLiteral (*ast, bitfield_bit_size_apint, ast->IntTy, SourceLocation());
|
bit_width = new (*ast)IntegerLiteral (*ast, bitfield_bit_size_apint, ast->IntTy, SourceLocation());
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjCIvarDecl *field = ObjCIvarDecl::Create (*ast,
|
field = ObjCIvarDecl::Create (*ast,
|
||||||
class_interface_decl,
|
class_interface_decl,
|
||||||
SourceLocation(),
|
SourceLocation(),
|
||||||
SourceLocation(),
|
SourceLocation(),
|
||||||
&identifier_table->get(name), // Identifier
|
&identifier_table->get(name), // Identifier
|
||||||
QualType::getFromOpaquePtr(ivar_opaque_type), // Field type
|
QualType::getFromOpaquePtr(ivar_opaque_type), // Field type
|
||||||
NULL, // TypeSourceInfo *
|
NULL, // TypeSourceInfo *
|
||||||
ConvertAccessTypeToObjCIvarAccessControl (access),
|
ConvertAccessTypeToObjCIvarAccessControl (access),
|
||||||
bit_width,
|
bit_width,
|
||||||
is_synthesized);
|
is_synthesized);
|
||||||
|
|
||||||
if (field)
|
if (field)
|
||||||
{
|
{
|
||||||
|
@ -2106,15 +2109,106 @@ ClangASTContext::AddObjCClassIVar
|
||||||
VerifyDecl(field);
|
VerifyDecl(field);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return true;
|
return field;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ClangASTContext::AddObjCClassProperty
|
||||||
|
(
|
||||||
|
ASTContext *ast,
|
||||||
|
clang_type_t class_opaque_type,
|
||||||
|
const char *property_name,
|
||||||
|
clang_type_t property_opaque_type,
|
||||||
|
ObjCIvarDecl *ivar_decl,
|
||||||
|
const char *property_setter_name,
|
||||||
|
const char *property_getter_name,
|
||||||
|
uint32_t property_attributes
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (class_opaque_type == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
IdentifierTable *identifier_table = &ast->Idents;
|
||||||
|
|
||||||
|
assert (ast != NULL);
|
||||||
|
assert (identifier_table != NULL);
|
||||||
|
|
||||||
|
QualType class_qual_type(QualType::getFromOpaquePtr(class_opaque_type));
|
||||||
|
const clang::Type *class_type = class_qual_type.getTypePtr();
|
||||||
|
if (class_type)
|
||||||
|
{
|
||||||
|
const ObjCObjectType *objc_class_type = dyn_cast<ObjCObjectType>(class_type);
|
||||||
|
|
||||||
|
if (objc_class_type)
|
||||||
|
{
|
||||||
|
ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface();
|
||||||
|
|
||||||
|
// FIXME: For now, we don't know how to add properties if we don't have their associated ivar.
|
||||||
|
if (class_interface_decl && ivar_decl)
|
||||||
|
{
|
||||||
|
clang::TypeSourceInfo *prop_type_source;
|
||||||
|
if (ivar_decl)
|
||||||
|
prop_type_source = ast->CreateTypeSourceInfo (ivar_decl->getType());
|
||||||
|
else
|
||||||
|
prop_type_source = ast->CreateTypeSourceInfo (QualType::getFromOpaquePtr(property_opaque_type));
|
||||||
|
|
||||||
|
ObjCPropertyDecl *property_decl = ObjCPropertyDecl::Create(*ast,
|
||||||
|
class_interface_decl,
|
||||||
|
SourceLocation(), // Source Location
|
||||||
|
&identifier_table->get(property_name),
|
||||||
|
SourceLocation(), //Source Location for AT
|
||||||
|
prop_type_source
|
||||||
|
);
|
||||||
|
if (property_decl)
|
||||||
|
{
|
||||||
|
class_interface_decl->addDecl (property_decl);
|
||||||
|
if (property_setter_name != NULL)
|
||||||
|
{
|
||||||
|
std::string property_setter_no_colon(property_setter_name, strlen(property_setter_name) - 1);
|
||||||
|
clang::IdentifierInfo *setter_ident = &identifier_table->get(property_setter_no_colon.c_str());
|
||||||
|
Selector setter_sel = ast->Selectors.getSelector(1, &setter_ident);
|
||||||
|
property_decl->setSetterName(setter_sel);
|
||||||
|
property_decl->setPropertyAttributes (clang::ObjCPropertyDecl::OBJC_PR_setter);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (property_getter_name != NULL)
|
||||||
|
{
|
||||||
|
clang::IdentifierInfo *getter_ident = &identifier_table->get(property_getter_name);
|
||||||
|
Selector getter_sel = ast->Selectors.getSelector(0, &getter_ident);
|
||||||
|
property_decl->setGetterName(getter_sel);
|
||||||
|
property_decl->setPropertyAttributes (clang::ObjCPropertyDecl::OBJC_PR_getter);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ivar_decl)
|
||||||
|
property_decl->setPropertyIvarDecl (ivar_decl);
|
||||||
|
|
||||||
|
if (property_attributes & DW_APPLE_PROPERTY_readonly)
|
||||||
|
property_decl->setPropertyAttributes (clang::ObjCPropertyDecl::OBJC_PR_readonly);
|
||||||
|
if (property_attributes & DW_APPLE_PROPERTY_readwrite)
|
||||||
|
property_decl->setPropertyAttributes (clang::ObjCPropertyDecl::OBJC_PR_readwrite);
|
||||||
|
if (property_attributes & DW_APPLE_PROPERTY_assign)
|
||||||
|
property_decl->setPropertyAttributes (clang::ObjCPropertyDecl::OBJC_PR_assign);
|
||||||
|
if (property_attributes & DW_APPLE_PROPERTY_retain)
|
||||||
|
property_decl->setPropertyAttributes (clang::ObjCPropertyDecl::OBJC_PR_retain);
|
||||||
|
if (property_attributes & DW_APPLE_PROPERTY_copy)
|
||||||
|
property_decl->setPropertyAttributes (clang::ObjCPropertyDecl::OBJC_PR_copy);
|
||||||
|
if (property_attributes & DW_APPLE_PROPERTY_nonatomic)
|
||||||
|
property_decl->setPropertyAttributes (clang::ObjCPropertyDecl::OBJC_PR_nonatomic);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ClangASTContext::ObjCTypeHasIVars (clang_type_t class_opaque_type, bool check_superclass)
|
ClangASTContext::ObjCTypeHasIVars (clang_type_t class_opaque_type, bool check_superclass)
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
LEVEL = ../../../make
|
||||||
|
|
||||||
|
OBJC_SOURCES := main.m
|
||||||
|
LDFLAGS = $(CFLAGS) -lobjc -framework Foundation
|
||||||
|
|
||||||
|
include $(LEVEL)/Makefile.rules
|
|
@ -0,0 +1,129 @@
|
||||||
|
"""
|
||||||
|
Use lldb Python API to verify that expression evaluation for property references uses the correct getters and setters
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os, time
|
||||||
|
import re
|
||||||
|
import unittest2
|
||||||
|
import lldb, lldbutil
|
||||||
|
from lldbtest import *
|
||||||
|
|
||||||
|
class ObjCDynamicValueTestCase(TestBase):
|
||||||
|
|
||||||
|
mydir = os.path.join("lang", "objc", "objc-property")
|
||||||
|
|
||||||
|
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
|
||||||
|
@python_api_test
|
||||||
|
def test_get_dynamic_objc_vals_with_dsym(self):
|
||||||
|
"""Test that expr uses the correct property getters and setters"""
|
||||||
|
self.buildDsym()
|
||||||
|
self.do_test_properties()
|
||||||
|
|
||||||
|
@python_api_test
|
||||||
|
def test_get_objc_dynamic_vals_with_dwarf(self):
|
||||||
|
"""Test that expr uses the correct property getters and setters"""
|
||||||
|
self.buildDwarf()
|
||||||
|
self.do_test_properties()
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
# Call super's setUp().
|
||||||
|
TestBase.setUp(self)
|
||||||
|
|
||||||
|
# Find the line number to break for main.c.
|
||||||
|
|
||||||
|
self.source_name = 'main.m'
|
||||||
|
|
||||||
|
def run_to_main (self):
|
||||||
|
"""Test that expr uses the correct property getters and setters"""
|
||||||
|
exe = os.path.join(os.getcwd(), "a.out")
|
||||||
|
|
||||||
|
# Create a target from the debugger.
|
||||||
|
|
||||||
|
target = self.dbg.CreateTarget (exe)
|
||||||
|
self.assertTrue(target, VALID_TARGET)
|
||||||
|
|
||||||
|
# Set up our breakpoints:
|
||||||
|
|
||||||
|
main_bkpt = target.BreakpointCreateBySourceRegex ("Set a breakpoint here.", lldb.SBFileSpec (self.source_name))
|
||||||
|
self.assertTrue(main_bkpt and
|
||||||
|
main_bkpt.GetNumLocations() == 1,
|
||||||
|
VALID_BREAKPOINT)
|
||||||
|
|
||||||
|
# Now launch the process, and do not stop at the entry point.
|
||||||
|
process = target.LaunchSimple (None, None, os.getcwd())
|
||||||
|
|
||||||
|
self.assertTrue(process.GetState() == lldb.eStateStopped,
|
||||||
|
PROCESS_STOPPED)
|
||||||
|
|
||||||
|
threads = lldbutil.get_threads_stopped_at_breakpoint (process, main_bkpt)
|
||||||
|
self.assertTrue (len(threads) == 1)
|
||||||
|
thread = threads[0]
|
||||||
|
return thread
|
||||||
|
|
||||||
|
def do_test_properties (self):
|
||||||
|
|
||||||
|
thread = self.run_to_main()
|
||||||
|
|
||||||
|
frame = thread.GetFrameAtIndex(0)
|
||||||
|
|
||||||
|
mine = frame.FindVariable ("mine")
|
||||||
|
self.assertTrue (mine.IsValid())
|
||||||
|
access_count = mine.GetChildMemberWithName ("_access_count")
|
||||||
|
self.assertTrue (access_count.IsValid())
|
||||||
|
start_access_count = access_count.GetValueAsUnsigned (123456)
|
||||||
|
self.assertTrue (start_access_count != 123456)
|
||||||
|
|
||||||
|
#
|
||||||
|
# The first set of tests test calling the getter & setter of
|
||||||
|
# a property that actually only has a getter & setter and no
|
||||||
|
# @property.
|
||||||
|
#
|
||||||
|
nonexistant_value = frame.EvaluateExpression("mine.nonexistantInt", False)
|
||||||
|
nonexistant_error = nonexistant_value.GetError()
|
||||||
|
self.assertTrue (nonexistant_error.Success())
|
||||||
|
nonexistant_int = nonexistant_value.GetValueAsUnsigned (123456)
|
||||||
|
self.assertTrue (nonexistant_int == 6)
|
||||||
|
|
||||||
|
# Calling the getter function would up the access count, so make sure that happened.
|
||||||
|
|
||||||
|
new_access_count = access_count.GetValueAsUnsigned (123456)
|
||||||
|
self.assertTrue (new_access_count - start_access_count == 1)
|
||||||
|
start_access_count = new_access_count
|
||||||
|
|
||||||
|
#
|
||||||
|
# Now call the setter, then make sure that
|
||||||
|
nonexistant_change = frame.EvaluateExpression("mine.nonexistantInt = 10", False)
|
||||||
|
nonexistant_error = nonexistant_change.GetError()
|
||||||
|
self.assertTrue (nonexistant_error.Success())
|
||||||
|
|
||||||
|
# Calling the setter function would up the access count, so make sure that happened.
|
||||||
|
|
||||||
|
new_access_count = access_count.GetValueAsUnsigned (123456)
|
||||||
|
self.assertTrue (new_access_count - start_access_count == 1)
|
||||||
|
start_access_count = new_access_count
|
||||||
|
|
||||||
|
#
|
||||||
|
# Now we call the getter of a property that is backed by an ivar,
|
||||||
|
# make sure it works and that we actually update the backing ivar.
|
||||||
|
#
|
||||||
|
|
||||||
|
backed_value = frame.EvaluateExpression("mine.backedInt", False)
|
||||||
|
backed_error = backed_value.GetError()
|
||||||
|
self.assertTrue (backed_error.Success())
|
||||||
|
backing_value = mine.GetChildMemberWithName ("_backedInt")
|
||||||
|
self.assertTrue (backing_value.IsValid())
|
||||||
|
self.assertTrue (backed_value.GetValueAsUnsigned (12345) == backing_value.GetValueAsUnsigned(23456))
|
||||||
|
|
||||||
|
#
|
||||||
|
# This doesn't work correctly yet, because the clang Sema::HandleExprPropertyRefExpr
|
||||||
|
# doesn't complete the class before looking up the property.
|
||||||
|
#
|
||||||
|
#unbacked_value = frame.EvaluateExpression("mine.unbackedInt", False)
|
||||||
|
#unbacked_error = unbacked_value.GetError()
|
||||||
|
#self.assertTrue (unbacked_error.Success())
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import atexit
|
||||||
|
lldb.SBDebugger.Initialize()
|
||||||
|
atexit.register(lambda: lldb.SBDebugger.Terminate())
|
||||||
|
unittest2.main()
|
|
@ -0,0 +1,91 @@
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
@interface BaseClass : NSObject
|
||||||
|
{
|
||||||
|
int _backedInt;
|
||||||
|
int _access_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (int) nonexistantInt;
|
||||||
|
- (void) setNonexistantInt: (int) in_int;
|
||||||
|
|
||||||
|
- (int) myGetUnbackedInt;
|
||||||
|
- (void) mySetUnbackedInt: (int) in_int;
|
||||||
|
|
||||||
|
- (int) getAccessCount;
|
||||||
|
|
||||||
|
+(BaseClass *) baseClassWithBackedInt: (int) inInt andUnbackedInt: (int) inOtherInt;
|
||||||
|
|
||||||
|
@property(getter=myGetUnbackedInt,setter=mySetUnbackedInt:) int unbackedInt;
|
||||||
|
@property int backedInt;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation BaseClass
|
||||||
|
@synthesize unbackedInt;
|
||||||
|
@synthesize backedInt = _backedInt;
|
||||||
|
|
||||||
|
+ (BaseClass *) baseClassWithBackedInt: (int) inInt andUnbackedInt: (int) inOtherInt
|
||||||
|
{
|
||||||
|
BaseClass *new = [[BaseClass alloc] init];
|
||||||
|
|
||||||
|
new->_backedInt = inInt;
|
||||||
|
new->unbackedInt = inOtherInt;
|
||||||
|
|
||||||
|
return new;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (int) myGetUnbackedInt
|
||||||
|
{
|
||||||
|
// NSLog (@"Getting BaseClass::unbackedInt - %d.\n", unbackedInt);
|
||||||
|
_access_count++;
|
||||||
|
return unbackedInt;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) mySetUnbackedInt: (int) in_int
|
||||||
|
{
|
||||||
|
// NSLog (@"Setting BaseClass::unbackedInt from %d to %d.", unbackedInt, in_int);
|
||||||
|
_access_count++;
|
||||||
|
unbackedInt = in_int;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (int) nonexistantInt
|
||||||
|
{
|
||||||
|
// NSLog (@"Getting BaseClass::nonexistantInt - %d.\n", 5);
|
||||||
|
_access_count++;
|
||||||
|
return 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) setNonexistantInt: (int) in_int
|
||||||
|
{
|
||||||
|
// NSLog (@"Setting BaseClass::nonexistantInt from 7 to %d.", in_int);
|
||||||
|
_access_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (int) getAccessCount
|
||||||
|
{
|
||||||
|
return _access_count;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
int
|
||||||
|
main ()
|
||||||
|
{
|
||||||
|
BaseClass *mine = [BaseClass baseClassWithBackedInt: 10 andUnbackedInt: 20];
|
||||||
|
|
||||||
|
// Set a breakpoint here.
|
||||||
|
int nonexistant = mine.nonexistantInt;
|
||||||
|
|
||||||
|
int backedInt = mine.backedInt;
|
||||||
|
|
||||||
|
int unbackedInt = mine.unbackedInt;
|
||||||
|
|
||||||
|
NSLog (@"Results for %p: nonexistant: %d backed: %d unbacked: %d accessCount: %d.",
|
||||||
|
mine,
|
||||||
|
nonexistant,
|
||||||
|
backedInt,
|
||||||
|
unbackedInt,
|
||||||
|
[mine getAccessCount]);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue