245 lines · python
1from lldbsuite.test.decorators import *2from lldbsuite.test.lldbtest import *3from lldbsuite.test import lldbutil4 5"""6This test ensures that we only create Clang AST nodes in our module AST7when we actually need them.8 9All tests in this file behave like this:10 1. Use LLDB to do something (expression evaluation, breakpoint setting, etc.).11 2. Check that certain Clang AST nodes were not loaded during the previous12 step.13"""14 15 16class TestCase(TestBase):17 NO_DEBUG_INFO_TESTCASE = True18 19 def setUp(self):20 TestBase.setUp(self)21 # Only build this test once.22 self.build()23 24 # Clang declaration kind we are looking for.25 class_decl_kind = "CXXRecordDecl"26 # FIXME: This shouldn't be a CXXRecordDecl, but that's how we model27 # structs in Clang.28 struct_decl_kind = "CXXRecordDecl"29 30 # The decls we use in this program in the format that31 # decl_in_line and decl_completed_in_line expect (which is a pair of32 # node type and the unqualified declaration name.33 struct_first_member_decl = [struct_decl_kind, "StructFirstMember"]34 struct_behind_ptr_decl = [struct_decl_kind, "StructBehindPointer"]35 struct_behind_ref_decl = [struct_decl_kind, "StructBehindRef"]36 struct_member_decl = [struct_decl_kind, "StructMember"]37 some_struct_decl = [struct_decl_kind, "SomeStruct"]38 other_struct_decl = [struct_decl_kind, "OtherStruct"]39 class_in_namespace_decl = [class_decl_kind, "ClassInNamespace"]40 class_we_enter_decl = [class_decl_kind, "ClassWeEnter"]41 class_member_decl = [struct_decl_kind, "ClassMember"]42 class_static_member_decl = [struct_decl_kind, "StaticClassMember"]43 unused_class_member_decl = [struct_decl_kind, "UnusedClassMember"]44 unused_class_member_ptr_decl = [struct_decl_kind, "UnusedClassMemberPtr"]45 46 def assert_no_decls_loaded(self):47 """48 Asserts that no known declarations in this test are loaded49 into the module's AST.50 """51 self.assert_decl_not_loaded(self.struct_first_member_decl)52 self.assert_decl_not_loaded(self.struct_behind_ptr_decl)53 self.assert_decl_not_loaded(self.struct_behind_ref_decl)54 self.assert_decl_not_loaded(self.struct_member_decl)55 self.assert_decl_not_loaded(self.some_struct_decl)56 self.assert_decl_not_loaded(self.other_struct_decl)57 self.assert_decl_not_loaded(self.class_in_namespace_decl)58 self.assert_decl_not_loaded(self.class_member_decl)59 self.assert_decl_not_loaded(self.class_static_member_decl)60 self.assert_decl_not_loaded(self.unused_class_member_decl)61 62 def get_ast_dump(self):63 """Returns the dumped Clang AST of the module as a string"""64 res = lldb.SBCommandReturnObject()65 ci = self.dbg.GetCommandInterpreter()66 ci.HandleCommand("target modules dump ast a.out", res)67 self.assertTrue(res.Succeeded())68 return res.GetOutput()69 70 def decl_in_line(self, line, decl):71 """72 Returns true iff the given line declares the given Clang decl.73 The line is expected to be in the form of Clang's AST dump.74 """75 line = line.rstrip() + "\n"76 decl_kind = "-" + decl[0] + " "77 # Either the decl is somewhere in the line or at the end of78 # the line.79 decl_name = " " + decl[1] + " "80 decl_name_eol = " " + decl[1] + "\n"81 if not decl_kind in line:82 return False83 return decl_name in line or decl_name_eol in line84 85 def decl_completed_in_line(self, line, decl):86 """87 Returns true iff the given line declares the given Clang decl and88 the decl was completed (i.e., it has no undeserialized declarations89 in it).90 """91 return (92 self.decl_in_line(line, decl)93 and not "<undeserialized declarations>" in line94 )95 96 # The following asserts are used for checking if certain Clang declarations97 # were loaded or not since the target was created.98 99 def assert_decl_loaded(self, decl):100 """101 Asserts the given decl is currently loaded.102 Note: This test is about checking that types/declarations are not103 loaded. If this assert fails it is usually fine to turn it into a104 assert_decl_not_loaded or assert_decl_not_completed assuming LLDB's105 functionality has not suffered by not loading this declaration.106 """107 ast = self.get_ast_dump()108 found = False109 for line in ast.splitlines():110 if self.decl_in_line(line, decl):111 found = True112 self.assertTrue(113 self.decl_completed_in_line(line, decl),114 "Should have called assert_decl_not_completed",115 )116 self.assertTrue(117 found, "Declaration no longer loaded " + str(decl) + ".\nAST:\n" + ast118 )119 120 def assert_decl_not_completed(self, decl):121 """122 Asserts that the given decl is currently not completed in the module's123 AST. It may be loaded but then can can only contain undeserialized124 declarations.125 """126 ast = self.get_ast_dump()127 found = False128 for line in ast.splitlines():129 error_msg = "Unexpected completed decl: '" + line + "'.\nAST:\n" + ast130 self.assertFalse(self.decl_completed_in_line(line, decl), error_msg)131 132 def assert_decl_not_loaded(self, decl):133 """134 Asserts that the given decl is currently not loaded in the module's135 AST.136 """137 ast = self.get_ast_dump()138 found = False139 for line in ast.splitlines():140 error_msg = "Unexpected loaded decl: '" + line + "'\nAST:\n" + ast141 self.assertFalse(self.decl_in_line(line, decl), error_msg)142 143 def clean_setup(self, location):144 """145 Runs to the line with the source line with the given location string146 and ensures that our module AST is empty.147 """148 lldbutil.run_to_source_breakpoint(149 self, "// Location: " + location, lldb.SBFileSpec("main.cpp")150 )151 # Make sure no declarations are loaded initially.152 self.assert_no_decls_loaded()153 154 @add_test_categories(["dwarf"])155 def test_arithmetic_expression_in_main(self):156 """Runs a simple arithmetic expression which should load nothing."""157 self.clean_setup(location="multiple locals function")158 159 self.expect("expr 1 + (int)2.0", substrs=["(int) $0"])160 161 # This should not have loaded any decls.162 self.assert_no_decls_loaded()163 164 @add_test_categories(["dwarf"])165 def test_printing_local_variable_in_other_struct_func(self):166 """167 Prints a local variable and makes sure no unrelated types are loaded.168 """169 self.clean_setup(location="other struct function")170 171 self.expect("expr other_struct_var", substrs=["(OtherStruct) $0"])172 # The decl we run on was loaded.173 self.assert_decl_loaded(self.other_struct_decl)174 175 # This should not have loaded anything else.176 self.assert_decl_not_loaded(self.some_struct_decl)177 self.assert_decl_not_loaded(self.class_in_namespace_decl)178 179 @add_test_categories(["dwarf"])180 def test_printing_struct_with_multiple_locals(self):181 """182 Prints a local variable and checks that we don't load other local183 variables.184 """185 self.clean_setup(location="multiple locals function")186 187 self.expect("expr struct_var", substrs=["(SomeStruct) $0"])188 189 # We loaded SomeStruct and its member types for printing.190 self.assert_decl_loaded(self.some_struct_decl)191 self.assert_decl_loaded(self.struct_behind_ptr_decl)192 self.assert_decl_loaded(self.struct_behind_ref_decl)193 194 # FIXME: We don't use these variables, but we seem to load all local195 # local variables.196 self.assert_decl_not_completed(self.other_struct_decl)197 self.assert_decl_not_completed(self.class_in_namespace_decl)198 199 @add_test_categories(["dwarf"])200 def test_addr_of_struct(self):201 """202 Prints the address of a local variable (which is a struct).203 """204 self.clean_setup(location="multiple locals function")205 206 self.expect("expr &struct_var", substrs=["(SomeStruct *) $0"])207 208 # We loaded SomeStruct.209 self.assert_decl_loaded(self.some_struct_decl)210 211 # The member declarations should not be completed.212 self.assert_decl_not_completed(self.struct_behind_ptr_decl)213 self.assert_decl_not_completed(self.struct_behind_ref_decl)214 215 # FIXME: The first member was behind a pointer so it shouldn't be216 # completed. Somehow LLDB really wants to load the first member, so217 # that is why have it defined here.218 self.assert_decl_loaded(self.struct_first_member_decl)219 220 # FIXME: We don't use these variables, but we seem to load all local221 # local variables.222 self.assert_decl_not_completed(self.other_struct_decl)223 self.assert_decl_not_completed(self.class_in_namespace_decl)224 225 @add_test_categories(["dwarf"])226 def test_class_function_access_member(self):227 self.clean_setup(location="class function")228 229 self.expect("expr member", substrs=["(ClassMember) $0"])230 231 # We loaded the current class we touched.232 self.assert_decl_loaded(self.class_we_enter_decl)233 # We loaded the unused members of this class.234 self.assert_decl_loaded(self.unused_class_member_decl)235 self.assert_decl_not_completed(self.unused_class_member_ptr_decl)236 # We loaded the member we used.237 self.assert_decl_loaded(self.class_member_decl)238 # We didn't load the type of the unused static member.239 self.assert_decl_not_completed(self.class_static_member_decl)240 241 # This should not have loaded anything else.242 self.assert_decl_not_loaded(self.other_struct_decl)243 self.assert_decl_not_loaded(self.some_struct_decl)244 self.assert_decl_not_loaded(self.class_in_namespace_decl)245