Index: packages/Python/lldbsuite/test/lang/objc/exceptions/Makefile =================================================================== --- packages/Python/lldbsuite/test/lang/objc/exceptions/Makefile +++ packages/Python/lldbsuite/test/lang/objc/exceptions/Makefile @@ -0,0 +1,9 @@ +LEVEL = ../../../make + +OBJC_SOURCES := main.m + +CFLAGS_EXTRAS += -w + +include $(LEVEL)/Makefile.rules + +LDFLAGS += -framework Foundation Index: packages/Python/lldbsuite/test/lang/objc/exceptions/TestObjCExceptions.py =================================================================== --- packages/Python/lldbsuite/test/lang/objc/exceptions/TestObjCExceptions.py +++ packages/Python/lldbsuite/test/lang/objc/exceptions/TestObjCExceptions.py @@ -0,0 +1,90 @@ +# encoding: utf-8 +""" +Test lldb Obj-C exception support. +""" + +from __future__ import print_function + + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class ObjCExceptionsTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipUnlessDarwin + def test_objc_exceptions_1(self): + self.build() + + target = self.dbg.CreateTarget(self.getBuildArtifact("a.out")) + self.assertTrue(target, VALID_TARGET) + + lldbutil.run_to_source_breakpoint(self, "// Set break point at this line.", lldb.SBFileSpec("main.m")) + + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs=['stopped', 'stop reason = breakpoint']) + + thread = self.dbg.GetSelectedTarget().GetProcess().GetSelectedThread() + frame = thread.GetSelectedFrame() + + self.expect( + 'frame variable e1', + substrs=[ + '(NSException *) e1 = ', + 'name: @"ExceptionName" - reason: @"SomeReason"' + ]) + + self.expect( + 'frame variable --dynamic-type no-run-target *e1', + substrs=[ + '(NSException) *e1 = ', + 'name = ', '@"ExceptionName"', + 'reason = ', '@"SomeReason"', + 'userInfo = ', '1 key/value pair', + 'reserved = ', 'nil', + ]) + + e1 = frame.FindVariable("e1") + self.assertTrue(e1) + self.assertEqual(e1.type.name, "NSException *") + self.assertEqual(e1.GetSummary(), 'name: @"ExceptionName" - reason: @"SomeReason"') + self.assertEqual(e1.GetChildMemberWithName("name").description, "ExceptionName") + self.assertEqual(e1.GetChildMemberWithName("reason").description, "SomeReason") + userInfo = e1.GetChildMemberWithName("userInfo").dynamic + self.assertEqual(userInfo.summary, "1 key/value pair") + self.assertEqual(userInfo.GetChildAtIndex(0).GetChildAtIndex(0).description, "some_key") + self.assertEqual(userInfo.GetChildAtIndex(0).GetChildAtIndex(1).description, "some_value") + self.assertEqual(e1.GetChildMemberWithName("reserved").description, "") + + self.expect( + 'frame variable e2', + substrs=[ + '(NSException *) e2 = ', + 'name: @"ThrownException" - reason: @"SomeReason"' + ]) + + self.expect( + 'frame variable --dynamic-type no-run-target *e2', + substrs=[ + '(NSException) *e2 = ', + 'name = ', '@"ThrownException"', + 'reason = ', '@"SomeReason"', + 'userInfo = ', '1 key/value pair', + 'reserved = ', + ]) + + e2 = frame.FindVariable("e2") + self.assertTrue(e2) + self.assertEqual(e2.type.name, "NSException *") + self.assertEqual(e2.GetSummary(), 'name: @"ThrownException" - reason: @"SomeReason"') + self.assertEqual(e2.GetChildMemberWithName("name").description, "ThrownException") + self.assertEqual(e2.GetChildMemberWithName("reason").description, "SomeReason") + userInfo = e2.GetChildMemberWithName("userInfo").dynamic + self.assertEqual(userInfo.summary, "1 key/value pair") + self.assertEqual(userInfo.GetChildAtIndex(0).GetChildAtIndex(0).description, "some_key") + self.assertEqual(userInfo.GetChildAtIndex(0).GetChildAtIndex(1).description, "some_value") + self.assertGreater(e2.GetChildMemberWithName("reserved").dynamic.num_children, 0) Index: packages/Python/lldbsuite/test/lang/objc/exceptions/main.m =================================================================== --- packages/Python/lldbsuite/test/lang/objc/exceptions/main.m +++ packages/Python/lldbsuite/test/lang/objc/exceptions/main.m @@ -0,0 +1,36 @@ +//===-- main.m ------------------------------------------------*- ObjC -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#import + +void foo() +{ + NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:@"some_value", @"some_key", nil]; + @throw [[NSException alloc] initWithName:@"ThrownException" reason:@"SomeReason" userInfo:info]; +} + +int main (int argc, const char * argv[]) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:@"some_value", @"some_key", nil]; + NSException *e1 = [[NSException alloc] initWithName:@"ExceptionName" reason:@"SomeReason" userInfo:info]; + NSException *e2; + + @try { + foo(); + } @catch(NSException *e) { + e2 = e; + } + + NSLog(@"1"); // Set break point at this line. + [pool drain]; + return 0; +} + Index: source/Plugins/Language/ObjC/NSException.cpp =================================================================== --- source/Plugins/Language/ObjC/NSException.cpp +++ source/Plugins/Language/ObjC/NSException.cpp @@ -30,8 +30,8 @@ using namespace lldb_private::formatters; static bool ExtractFields(ValueObject &valobj, ValueObjectSP *name_sp, - ValueObjectSP *reason_sp, - ValueObjectSP *userinfo_sp) { + ValueObjectSP *reason_sp, ValueObjectSP *userinfo_sp, + ValueObjectSP *reserved_sp) { ProcessSP process_sp(valobj.GetProcessSP()); if (!process_sp) return false; @@ -61,10 +61,14 @@ auto userinfo = process_sp->ReadPointerFromMemory(ptr + 3 * ptr_size, error); if (error.Fail() || userinfo == LLDB_INVALID_ADDRESS) return false; + auto reserved = process_sp->ReadPointerFromMemory(ptr + 4 * ptr_size, error); + if (error.Fail() || reserved == LLDB_INVALID_ADDRESS) + return false; InferiorSizedWord name_isw(name, *process_sp); InferiorSizedWord reason_isw(reason, *process_sp); InferiorSizedWord userinfo_isw(userinfo, *process_sp); + InferiorSizedWord reserved_isw(reserved, *process_sp); CompilerType voidstar = process_sp->GetTarget() .GetScratchClangASTContext() @@ -83,6 +87,10 @@ *userinfo_sp = ValueObject::CreateValueObjectFromData( "userInfo", userinfo_isw.GetAsData(process_sp->GetByteOrder()), valobj.GetExecutionContextRef(), voidstar); + if (reserved_sp) + *reserved_sp = ValueObject::CreateValueObjectFromData( + "reserved", reserved_isw.GetAsData(process_sp->GetByteOrder()), + valobj.GetExecutionContextRef(), voidstar); return true; } @@ -91,7 +99,7 @@ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { lldb::ValueObjectSP name_sp; lldb::ValueObjectSP reason_sp; - if (!ExtractFields(valobj, &name_sp, &reason_sp, nullptr)) + if (!ExtractFields(valobj, &name_sp, &reason_sp, nullptr, nullptr)) return false; if (!name_sp || !reason_sp) @@ -117,19 +125,27 @@ ~NSExceptionSyntheticFrontEnd() override = default; size_t CalculateNumChildren() override { - return 1; + return 4; } lldb::ValueObjectSP GetChildAtIndex(size_t idx) override { switch (idx) { - case 0: return m_userinfo_sp; + case 0: return m_name_sp; + case 1: return m_reason_sp; + case 2: return m_userinfo_sp; + case 3: return m_reserved_sp; } return lldb::ValueObjectSP(); } bool Update() override { + m_name_sp.reset(); + m_reason_sp.reset(); m_userinfo_sp.reset(); - if (!ExtractFields(m_backend, nullptr, nullptr, &m_userinfo_sp)) { + m_reserved_sp.reset(); + + if (!ExtractFields(m_backend, &m_name_sp, &m_reason_sp, &m_userinfo_sp, + &m_reserved_sp)) { return false; } return true; @@ -138,14 +154,27 @@ bool MightHaveChildren() override { return true; } size_t GetIndexOfChildWithName(const ConstString &name) override { + // NSException has 4 members: + // NSString *name; + // NSString *reason; + // NSDictionary *userInfo; + // id reserved; + static ConstString g___name("name"); + static ConstString g___reason("reason"); static ConstString g___userInfo("userInfo"); - if (name == g___userInfo) - return 0; + static ConstString g___reserved("reserved"); + if (name == g___name) return 0; + if (name == g___reason) return 1; + if (name == g___userInfo) return 2; + if (name == g___reserved) return 3; return UINT32_MAX; } private: + ValueObjectSP m_name_sp; + ValueObjectSP m_reason_sp; ValueObjectSP m_userinfo_sp; + ValueObjectSP m_reserved_sp; }; SyntheticChildrenFrontEnd *