Fixing an issue where Unicode characters in an NSString were printed as escape sequences by the summary provider shipping with LLDB - Added relevant test case code. Bonus points for identifying the source of the quotes :-)

llvm-svn: 153624
This commit is contained in:
Enrico Granata 2012-03-29 01:34:34 +00:00
parent b474099e63
commit 86ea8d821a
4 changed files with 77 additions and 11 deletions

View File

@ -14,7 +14,11 @@ def CFString_SummaryProvider (valobj,dict):
provider = CFStringSynthProvider(valobj,dict);
if provider.invalid == False:
try:
summary = provider.get_child_at_index(provider.get_child_index("content")).GetSummary();
summary = provider.get_child_at_index(provider.get_child_index("content"))
if type(summary) == lldb.SBValue:
summary = summary.GetSummary()
else:
summary = '"' + summary + '"'
except:
summary = None
if summary == None:
@ -111,20 +115,19 @@ class CFStringSynthProvider:
# a full pointer is skipped here before getting to the live data
pointer = pointer + self.pointer_size
else:
pointer = self.valobj.GetValueAsUnsigned(0) + self.size_of_cfruntime_base();
pointer = self.valobj.GetValueAsUnsigned(0) + self.size_of_cfruntime_base()
# read 8 bytes here and make an address out of them
try:
vopointer = self.valobj.CreateChildAtOffset("dummy",
pointer,self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar).GetPointerType());
pointer = vopointer.GetValueAsUnsigned(0)
char_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar).GetPointerType()
vopointer = self.valobj.CreateValueFromAddress("dummy",pointer,char_type);
pointer = vopointer.GetValueAsUnsigned(0)
except:
return self.valobj.CreateValueFromExpression("content",
return self.valobj.CreateValueFromExpression("content",
'(char*)"@\"invalid NSString\""')
# step 2: read Unicode data at pointer
pystr = self.read_unicode(pointer)
# step 3: return it
return self.valobj.CreateValueFromExpression("content",
"(char*)\"" + pystr.encode('utf-8') + "\"")
return pystr.encode('utf-8')
def handle_inline_explicit(self):
offset = 3*self.pointer_size
@ -177,6 +180,11 @@ class CFStringSynthProvider:
# if this is not possible, a new flag might have to be made up (like the "special" flag
# below, which is not a real flag in CFString), or alternatively one might need to use
# the ObjC runtime helper to detect the new class and deal with it accordingly
#print 'mutable = ' + str(self.mutable)
#print 'inline = ' + str(self.inline)
#print 'explicit = ' + str(self.explicit)
#print 'unicode = ' + str(self.unicode)
#print 'special = ' + str(self.special)
if self.mutable == True:
return self.handle_mutable_string()
elif self.inline == True and self.explicit == True and \

View File

@ -2322,7 +2322,7 @@ ValueObject::GetValuesForExpressionPath(const char* expression,
if (!ret_val.get()) // if there are errors, I add nothing to the list
return 0;
if (*reason_to_stop != eExpressionPathScanEndReasonArrayRangeOperatorMet)
if ( (reason_to_stop ? *reason_to_stop : dummy_reason_to_stop) != eExpressionPathScanEndReasonArrayRangeOperatorMet)
{
// I need not expand a range, just post-process the final value and return
if (!final_task_on_target || *final_task_on_target == ValueObject::eExpressionPathAftermathNothing)
@ -2330,7 +2330,7 @@ ValueObject::GetValuesForExpressionPath(const char* expression,
list->Append(ret_val);
return 1;
}
if (ret_val.get() && *final_value_type == eExpressionPathEndResultTypePlain) // I can only deref and takeaddress of plain objects
if (ret_val.get() && (final_value_type ? *final_value_type : dummy_final_value_type) == eExpressionPathEndResultTypePlain) // I can only deref and takeaddress of plain objects
{
if (*final_task_on_target == ValueObject::eExpressionPathAftermathDereference)
{

View File

@ -1,3 +1,4 @@
# encoding: utf-8
"""
Test lldb data formatter subsystem.
"""
@ -60,6 +61,18 @@ class ObjCDataFormatterTestCase(TestBase):
self.buildDwarf()
self.kvo_data_formatter_commands()
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
def test_rdar11106605_with_dsym_and_run_command(self):
"""Check that Unicode characters come out of CFString summary correctly."""
self.buildDsym()
self.rdar11106605_commands()
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
def test_rdar11106605_with_dwarf_and_run_command(self):
"""Check that Unicode characters come out of CFString summary correctly."""
self.buildDwarf()
self.rdar11106605_commands()
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
@expectedFailurei386
def test_expr_with_dsym_and_run_command(self):
@ -80,6 +93,44 @@ class ObjCDataFormatterTestCase(TestBase):
# Find the line number to break at.
self.line = line_number('main.m', '// Set break point at this line.')
def rdar11106605_commands(self):
"""Check that Unicode characters come out of CFString summary correctly."""
self.runCmd("file a.out", CURRENT_EXECUTABLE_SET)
self.expect("breakpoint set -f main.m -l %d" % self.line,
BREAKPOINT_CREATED,
startstr = "Breakpoint created: 1: file ='main.m', line = %d, locations = 1" %
self.line)
self.runCmd("run", RUN_SUCCEEDED)
# The stop reason of the thread should be breakpoint.
self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
substrs = ['stopped',
'stop reason = breakpoint'])
# This is the function to remove the custom formats in order to have a
# clean slate for the next test case.
def cleanup():
self.runCmd('type format clear', check=False)
self.runCmd('type summary clear', check=False)
self.runCmd('type synth clear', check=False)
self.runCmd('type category disable CoreFoundation', check=False)
self.runCmd('type category disable CoreGraphics', check=False)
self.runCmd('type category disable CoreServices', check=False)
self.runCmd('type category disable AppKit', check=False)
# Execute the cleanup function during test case tear down.
self.addTearDownHook(cleanup)
self.runCmd("type category enable AppKit")
self.expect('frame variable italian', substrs = ['L\'Italia è una Repubblica democratica, fondata sul lavoro. La sovranità appartiene al popolo, che la esercita nelle forme e nei limiti della Costituzione.'])
self.expect('frame variable french', substrs = ['Que veut cette horde d\'esclaves, De traîtres, de rois conjurés?'])
self.expect('frame variable german', substrs = ['Über-Ich und aus den Ansprüchen der sozialen Umwelt'])
self.expect('frame variable japanese', substrs = ['色は匂へど散りぬるを'])
def plain_data_formatter_commands(self):
"""Test basic ObjC formatting behavior."""
self.runCmd("file a.out", CURRENT_EXECUTABLE_SET)
@ -197,7 +248,7 @@ class ObjCDataFormatterTestCase(TestBase):
'(NSString *) str6 = ',' @"1ST"',
'(NSString *) str8 = ',' @"hasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTimehasVeryLongExtensionThisTime',
'(NSString *) str9 = ',' @"a very much boring task to write a string this way!!',
'(NSString *) str10 = ',' @"This is a Unicode string',
'(NSString *) str10 = ',' @"This is a Unicode string σ number 4 right here"',
'(NSString *) str11 = ',' @"__NSCFString"',
'(NSString *) label1 = ',' @"Process Name: "',
'(NSString *) label2 = ',' @"Process Id: "',

View File

@ -277,6 +277,13 @@ int main (int argc, const char * argv[])
NSString *strD12 = [NSString stringWithFormat:@"%@ %@ %@ %@", label1, processName, label2, processID];
NSString *eAcute = [NSString stringWithFormat: @"%C", 0x00E9];
NSString *randomHaziChar = [NSString stringWithFormat: @"%C", 0x9DC5];
NSString *japanese = @"色は匂へど散りぬるを";
NSString *italian = @"L'Italia è una Repubblica democratica, fondata sul lavoro. La sovranità appartiene al popolo, che la esercita nelle forme e nei limiti della Costituzione.";
NSString* french = @"Que veut cette horde d'esclaves, De traîtres, de rois conjurés?";
NSString* german = @"Über-Ich und aus den Ansprüchen der sozialen Umwelt";
void* data_set[3] = {str1,str2,str3};
NSArray* newArray = [[NSMutableArray alloc] init];