llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-lldb Author: Raphael Isemann (Teemperor) <details> <summary>Changes</summary> Replace most of the test's setup code with modern test utils and expand the test case to cover more common or potentially problematic uses of unions. --- Full diff: https://github.com/llvm/llvm-project/pull/208662.diff 2 Files Affected: - (modified) lldb/test/API/lang/c/unions/TestUnionMembers.py (+58-33) - (modified) lldb/test/API/lang/c/unions/main.c (+58-13) ``````````diff diff --git a/lldb/test/API/lang/c/unions/TestUnionMembers.py b/lldb/test/API/lang/c/unions/TestUnionMembers.py index d9acc00f8269e..263f76a313ecf 100644 --- a/lldb/test/API/lang/c/unions/TestUnionMembers.py +++ b/lldb/test/API/lang/c/unions/TestUnionMembers.py @@ -1,44 +1,69 @@ import lldb +from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * -import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test import lldbutil -class TestUnionMembers(TestBase): - def test_union_members(self): - self._load_exe() +class TestCase(TestBase): + def test(self): + self.build_and_run() - # Set breakpoints - bp = self.target.BreakpointCreateBySourceRegex("Break here", self.src_file_spec) - self.assertTrue(bp.IsValid() and bp.GetNumLocations() >= 1, VALID_BREAKPOINT) + # Reinterpreting a larger member through smaller ones depends on the + # host byte order. The values below assume little-endian; on big-endian + # hosts those assertions are skipped. + # TODO: Add test assertions for big endian. + little_endian = self.target().GetByteOrder() == lldb.eByteOrderLittle - # Launch the process - self.process = self.target.LaunchSimple( - None, None, self.get_process_working_directory() + # Type punning: a float and an integer sharing the same 4 bytes. Both + # members span the whole storage, so this is endian-independent. + self.expect_var_path("fb", type="FloatBits") + self.expect_var_path("fb.f", type="float", value="1") + self.expect_var_path("fb.bits", type="unsigned int", value="1065353216") + self.expect_expr( + "fb.bits", result_type="unsigned int", result_value="1065353216" ) - self.assertTrue(self.process.IsValid(), PROCESS_IS_VALID) - self.assertEqual(self.process.GetState(), lldb.eStateStopped, PROCESS_STOPPED) - thread = lldbutil.get_stopped_thread(self.process, lldb.eStopReasonBreakpoint) - self.assertTrue(thread.IsValid()) - frame = thread.GetSelectedFrame() - self.assertTrue(frame.IsValid()) + # Basic aliasing: the same int seen as two shorts and as a single byte. + self.expect_var_path("basic", type="Basic") + self.expect_var_path("basic.n", type="int", value="287454020") + self.expect_var_path("basic.halves", type="unsigned short[2]") + self.expect_expr("basic.n", result_type="int", result_value="287454020") + if little_endian: + self.expect_var_path( + "basic.halves[0]", type="unsigned short", value="13124" + ) + self.expect_var_path("basic.halves[1]", type="unsigned short", value="4386") + self.expect_var_path("basic.byte", type="unsigned char", value="'D'") - val = frame.EvaluateExpression("u") - self.assertTrue(val.IsValid()) - val = frame.EvaluateExpression("u.s") - self.assertTrue(val.IsValid()) - self.assertEqual(val.GetNumChildren(), 2) + # A union whose storage is also described by a struct. + self.expect_var_path("ws", type="WithStruct") + self.expect_var_path("ws.packed", type="int", value="131073") + self.expect_var_path("ws.view", type="Halves") + if little_endian: + self.expect_var_path("ws.view.lo", type="short", value="1") + self.expect_var_path("ws.view.hi", type="short", value="2") - def _load_exe(self): - self.build() - - src_file = os.path.join(self.getSourceDir(), "main.c") - self.src_file_spec = lldb.SBFileSpec(src_file) - self.assertTrue(self.src_file_spec.IsValid(), "breakpoint file") - - # Get the path of the executable - exe_path = self.getBuildArtifact("a.out") + # A union nested inside another union. Both inner members sit at offset + # 0, so they alias each other regardless of byte order. + self.expect_var_path("nested", type="Nested") + self.expect_var_path("nested.all", type="int", value="393221") + self.assertEqual( + self.frame().GetValueForVariablePath("nested.inner.a").GetValueAsSigned(), + self.frame().GetValueForVariablePath("nested.inner.b").GetValueAsSigned(), + ) + if little_endian: + self.expect_var_path("nested.inner.a", type="short", value="5") + self.expect_var_path("nested.inner.b", type="short", value="5") - # Load the executable - self.target = self.dbg.CreateTarget(exe_path) - self.assertTrue(self.target.IsValid(), VALID_TARGET) + # Members of an anonymous union are reached directly on the struct. + self.expect_var_path("anon", type="WithAnonUnion") + self.expect_var_path("anon.tag", type="int", value="42") + self.expect_var_path("anon.as_int", type="int", value="67305985") + self.expect_var_path("anon.as_bytes", type="unsigned char[4]") + if little_endian: + for index, byte in enumerate([1, 2, 3, 4]): + self.expect_var_path( + f"anon.as_bytes[{index}]", + type="unsigned char", + value=f"'\\x0{byte}'", + ) diff --git a/lldb/test/API/lang/c/unions/main.c b/lldb/test/API/lang/c/unions/main.c index 2c6a7d1e78211..5c89d3bd5305a 100644 --- a/lldb/test/API/lang/c/unions/main.c +++ b/lldb/test/API/lang/c/unions/main.c @@ -1,18 +1,63 @@ -#include <stdint.h> +// Type punning: reinterpret the bits of a float as an integer. Both members +// span the same 4 bytes, so the observed values don't depend on endianness. +union FloatBits { + float f; + unsigned int bits; +}; -union S -{ - int32_t n; // occupies 4 bytes - uint16_t s[2]; // occupies 4 bytes - uint8_t c; // occupies 1 byte -}; // the whole union occupies 4 bytes +// Basic aliasing: the same 4 bytes seen as different types and sizes. Reading +// the storage back through the smaller members depends on the host byte order. +union Basic { + int n; + unsigned short halves[2]; + unsigned char byte; +}; -int main() -{ - union S u; +// A union whose storage is also described by a struct ("view" pattern). +struct Halves { + short lo; + short hi; +}; +union WithStruct { + int packed; + struct Halves view; +}; - u.s[0] = 1234; - u.s[1] = 4321; +// A union nested directly inside another union. +union Nested { + int all; + union { + short a; + short b; + } inner; +}; - return 0; // Break here +// A struct with an anonymous union member; the union's members are accessed as +// if they were fields of the enclosing struct. +struct WithAnonUnion { + int tag; + union { + int as_int; + unsigned char as_bytes[4]; + }; +}; + +int main() { + union FloatBits fb; + fb.f = 1.0f; + + union Basic basic; + basic.n = 0x11223344; + + union WithStruct ws; + ws.packed = 0x00020001; + + union Nested nested; + nested.all = 0x00060005; + + struct WithAnonUnion anon; + anon.tag = 42; + anon.as_int = 0x04030201; + + return 0; // break here } `````````` </details> https://github.com/llvm/llvm-project/pull/208662 _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
