jimingham 3c8e9499ac
Handle accelerated ObjC Class method dispatchs. (#188392)
On Darwin, clang added a new objc class method dispatch, turned on by
the flag: `-fobjc-msgsend-class-selector-stubs`. These stubs are like
the accelerated stubs added previously for method selectors. This patch
adds support for stepping through them.
I had previously handled the method stubs by figuring out the object ->
class and then used the selector encoded in the stub name. But that
depended on all the stubs setting the object into arg1 before calling
the stub, which not all the stubs do anymore. Since all the stubs do
some work and then call objc_msgSend, instead of trying to predict what
the stub is going to do, I just let it run to the objc_msgSend and then
figure out how to go from there - at that point arg1 and arg2 are
correctly set, so I can use the regular objc_msgSend trampoline for
that.

That change meant I was no longer using the "find the implementation"
method that was passing in the selector string that we pulled from the
stub name, so this patch also removes that.
2026-03-26 09:37:50 -07:00

65 lines
2.2 KiB
Python

"""Test calling functions in class methods."""
import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
class TestObjCClassMethod(TestBase):
def setUp(self):
# Call super's setUp().
TestBase.setUp(self)
# Find the line numbers to break inside main().
self.main_source = lldb.SBFileSpec("class.m")
SHARED_BUILD_TESTCASE = False
NO_DEBUG_INFO_TESTCASE = True
@skipUnlessDarwin
@skipUnlessCompilerSupports("-fobjc-msgsend-class-selector-stubs")
@add_test_categories(["pyapi"])
def test_without_class_stubs(self):
self.do_test_with_python_api("-fno-objc-msgsend-class-selector-stubs")
@skipUnlessDarwin
@skipUnlessCompilerSupports("-fobjc-msgsend-class-selector-stubs")
@add_test_categories(["pyapi"])
def test_using_class_stubs(self):
self.do_test_with_python_api("-fobjc-msgsend-class-selector-stubs")
def do_test_with_python_api(self, compiler_flags):
"""Test calling functions in class methods."""
d = {}
if len(compiler_flags):
d["CFLAGS_EXTRAS"] = compiler_flags
self.build(dictionary=d)
target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(
self, "Set a breakpoint here", self.main_source
)
# Now make sure we can call a function in the class method we've
# stopped in.
frame = thread.GetFrameAtIndex(0)
self.assertTrue(frame, "Got a valid frame 0 frame.")
# First check that we can call a class method:
cmd_value = frame.EvaluateExpression(
'(int)[Foo doSomethingWithString:@"Hello"]'
)
if self.TraceOn():
if cmd_value.IsValid():
print("cmd_value is valid")
print("cmd_value has the value %d" % cmd_value.GetValueAsUnsigned())
self.assertTrue(cmd_value.IsValid())
self.assertEqual(cmd_value.GetValueAsUnsigned(), 5)
# Now check that we can step INTO class methods:
thread.StepInto()
frame = thread.GetFrameAtIndex(0)
self.assertEqual(
frame.name, "+[Foo doSomethingWithString:]", "Stopped in class method"
)