LLDB_LOG(log, " CEDM::FEVD Adding type for $__lldb_class: {1}",
class_qual_type.getAsString());
- AddContextClassType(context, class_user_type);
+ AddContextClassType(context, class_user_type, method_decl);
if (method_decl->isInstance()) {
// self is a pointer to the object
}
}
-void ClangExpressionDeclMap::AddContextClassType(NameSearchContext &context,
- const TypeFromUser &ut) {
+void ClangExpressionDeclMap::AddContextClassType(
+ NameSearchContext &context, const TypeFromUser &ut,
+ CXXMethodDecl *context_method) {
CompilerType copied_clang_type = GuardedCopyType(ut);
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
void_clang_type, &void_ptr_clang_type, 1, false, 0);
const bool is_virtual = false;
- const bool is_static = false;
+ // If we evaluate an expression inside a static method, we also need to
+ // make our lldb_expr method static so that Clang denies access to
+ // non-static members.
+ // If we don't have a context_method we are evaluating within a context
+ // object and we can allow access to non-static members.
+ const bool is_static = context_method ? context_method->isStatic() : false;
const bool is_inline = false;
const bool is_explicit = false;
const bool is_attr_used = true;
///
/// \param[in] type
/// The type of the class that serves as the evaluation context.
- void AddContextClassType(NameSearchContext &context,
- const TypeFromUser &type);
+ ///
+ /// \param[in] context_method
+ /// The member function declaration in which the expression is being
+ /// evaluated or null if the expression is not evaluated in the context
+ /// of a member function.
+ void AddContextClassType(NameSearchContext &context, const TypeFromUser &type,
+ clang::CXXMethodDecl *context_method = nullptr);
/// Move a type out of the current ASTContext into another, but make sure to
/// export all components of the type also.
module_imports.c_str(), m_name.c_str(),
lldb_local_var_decls.GetData(), tagged_body.c_str());
break;
+ case WrapKind::CppStaticMemberFunction:
case WrapKind::CppMemberFunction:
wrap_stream.Printf("%s"
"void \n"
enum class WrapKind {
/// Wrapped in a non-static member function of a C++ class.
CppMemberFunction,
+ /// Wrapped in a static member function of a C++ class.
+ CppStaticMemberFunction,
/// Wrapped in an instance Objective-C method.
ObjCInstanceMethod,
/// Wrapped in a static Objective-C method.
ObjCStaticMethod,
/// Wrapped in a non-member function.
- /// Note that this is also used for static member functions of a C++ class.
Function
};
m_needs_object_ptr = true;
} else if (clang::CXXMethodDecl *method_decl =
TypeSystemClang::DeclContextGetAsCXXMethodDecl(decl_context)) {
- if (m_allow_cxx && method_decl->isInstance()) {
- if (m_enforce_valid_object) {
- lldb::VariableListSP variable_list_sp(
- function_block->GetBlockVariableList(true));
+ if (m_allow_cxx) {
+ if (method_decl->isInstance()) {
+ if (m_enforce_valid_object) {
+ lldb::VariableListSP variable_list_sp(
+ function_block->GetBlockVariableList(true));
- const char *thisErrorString = "Stopped in a C++ method, but 'this' "
- "isn't available; pretending we are in a "
- "generic context";
+ const char *thisErrorString =
+ "Stopped in a C++ method, but 'this' "
+ "isn't available; pretending we are in a "
+ "generic context";
- if (!variable_list_sp) {
- err.SetErrorString(thisErrorString);
- return;
- }
+ if (!variable_list_sp) {
+ err.SetErrorString(thisErrorString);
+ return;
+ }
- lldb::VariableSP this_var_sp(
- variable_list_sp->FindVariable(ConstString("this")));
+ lldb::VariableSP this_var_sp(
+ variable_list_sp->FindVariable(ConstString("this")));
- if (!this_var_sp || !this_var_sp->IsInScope(frame) ||
- !this_var_sp->LocationIsValidForFrame(frame)) {
- err.SetErrorString(thisErrorString);
- return;
+ if (!this_var_sp || !this_var_sp->IsInScope(frame) ||
+ !this_var_sp->LocationIsValidForFrame(frame)) {
+ err.SetErrorString(thisErrorString);
+ return;
+ }
}
+ m_needs_object_ptr = true;
}
-
m_in_cplusplus_method = true;
- m_needs_object_ptr = true;
+ m_in_static_method = !method_decl->isInstance();
}
} else if (clang::ObjCMethodDecl *method_decl =
TypeSystemClang::DeclContextGetAsObjCMethodDecl(
assert(m_options.GetExecutionPolicy() != eExecutionPolicyTopLevel &&
"Top level expressions aren't wrapped.");
using Kind = ClangExpressionSourceCode::WrapKind;
- if (m_in_cplusplus_method)
+ if (m_in_cplusplus_method) {
+ if (m_in_static_method)
+ return Kind::CppStaticMemberFunction;
return Kind::CppMemberFunction;
- else if (m_in_objectivec_method) {
+ } else if (m_in_objectivec_method) {
if (m_in_static_method)
return Kind::ObjCStaticMethod;
return Kind::ObjCInstanceMethod;
bool m_in_objectivec_method = false;
/// True if the expression is compiled as a static (or class) method
/// (currently true if it was parsed when exe_ctx was in an Objective-C class
- /// method).
+ /// method or static C++ member function).
bool m_in_static_method = false;
/// True if "this" or "self" must be looked up and passed in. False if the
/// expression doesn't really use them and they can be NULL.
--- /dev/null
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
--- /dev/null
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class TestCase(TestBase):
+
+ mydir = TestBase.compute_mydir(__file__)
+
+ @no_debug_info_test
+ def test(self):
+ self.build()
+ lldbutil.run_to_source_breakpoint(self, "// break in static member function", lldb.SBFileSpec("main.cpp"))
+
+ # Evaluate a static member and call a static member function.
+ self.expect_expr("static_member_var", result_type="int", result_value="2")
+ self.expect_expr("static_const_member_var", result_type="const int", result_value="3")
+ self.expect_expr("static_constexpr_member_var", result_type="const int", result_value="4")
+ self.expect_expr("static_func()", result_type="int", result_value="6")
+
+ # Check that accessing non-static members just reports a diagnostic.
+ self.expect("expr member_var", error=True,
+ substrs=["invalid use of member 'member_var' in static member function"])
+ self.expect("expr member_func()", error=True,
+ substrs=["call to non-static member function without an object argument"])
+ self.expect("expr this", error=True,
+ substrs=["invalid use of 'this' outside of a non-static member function"])
+
+ # Continue to a non-static member function of the same class and make
+ # sure that evaluating non-static members now works.
+ breakpoint = self.target().BreakpointCreateBySourceRegex(
+ "// break in member function", lldb.SBFileSpec("main.cpp"))
+ self.assertNotEqual(breakpoint.GetNumResolvedLocations(), 0)
+ stopped_threads = lldbutil.continue_to_breakpoint(self.process(), breakpoint)
+
+ self.expect_expr("member_var", result_type="int", result_value="1")
+ self.expect_expr("member_func()", result_type="int", result_value="5")
--- /dev/null
+struct A {
+ int member_var = 1;
+ static int static_member_var;
+ static const int static_const_member_var;
+ static constexpr int static_constexpr_member_var = 4;
+ int member_func() { return 5; }
+ static int static_func() { return 6; }
+
+ static int context_static_func() {
+ int i = static_member_var;
+ i += static_func();
+ return i; // break in static member function
+ }
+
+ int context_member_func() {
+ int i = member_var;
+ i += member_func();
+ return i; // break in member function
+ }
+};
+
+int A::static_member_var = 2;
+const int A::static_const_member_var = 3;
+constexpr int A::static_constexpr_member_var;
+
+int main() {
+ int i = A::context_static_func();
+ A a;
+ a.context_member_func();
+ return i;
+}