labath created this revision. labath added a reviewer: spyffe. labath added subscribers: lldb-commits, paulherman, sivachandra.
referencing a user-defined operator new was triggering an assert in clang because we were registering the function name as string "operator new", instead of using the special operator enum, which clang has for this purpose. Method operators already had code to handle this, and now I extend this to cover free standing operator functions as well. Test included. http://reviews.llvm.org/D17856 Files: include/lldb/Symbol/ClangASTContext.h packages/Python/lldbsuite/test/lang/cpp/global_operators/TestCppGlobalOperators.py packages/Python/lldbsuite/test/lang/cpp/global_operators/main.cpp source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp source/Symbol/ClangASTContext.cpp
Index: source/Symbol/ClangASTContext.cpp =================================================================== --- source/Symbol/ClangASTContext.cpp +++ source/Symbol/ClangASTContext.cpp @@ -123,6 +123,241 @@ return *g_map_ptr; } +static bool +IsOperator(const char *name, clang::OverloadedOperatorKind &op_kind) +{ + if (name == nullptr || name[0] == '\0') + return false; + +#define OPERATOR_PREFIX "operator" +#define OPERATOR_PREFIX_LENGTH (sizeof(OPERATOR_PREFIX) - 1) + + const char *post_op_name = nullptr; + + bool no_space = true; + + if (::strncmp(name, OPERATOR_PREFIX, OPERATOR_PREFIX_LENGTH)) + return false; + + post_op_name = name + OPERATOR_PREFIX_LENGTH; + + if (post_op_name[0] == ' ') + { + post_op_name++; + no_space = false; + } + +#undef OPERATOR_PREFIX +#undef OPERATOR_PREFIX_LENGTH + + // This is an operator, set the overloaded operator kind to invalid + // in case this is a conversion operator... + op_kind = clang::NUM_OVERLOADED_OPERATORS; + + switch (post_op_name[0]) + { + default: + if (no_space) + return false; + break; + case 'n': + if (no_space) + return false; + if (strcmp(post_op_name, "new") == 0) + op_kind = clang::OO_New; + else if (strcmp(post_op_name, "new[]") == 0) + op_kind = clang::OO_Array_New; + break; + + case 'd': + if (no_space) + return false; + if (strcmp(post_op_name, "delete") == 0) + op_kind = clang::OO_Delete; + else if (strcmp(post_op_name, "delete[]") == 0) + op_kind = clang::OO_Array_Delete; + break; + + case '+': + if (post_op_name[1] == '\0') + op_kind = clang::OO_Plus; + else if (post_op_name[2] == '\0') + { + if (post_op_name[1] == '=') + op_kind = clang::OO_PlusEqual; + else if (post_op_name[1] == '+') + op_kind = clang::OO_PlusPlus; + } + break; + + case '-': + if (post_op_name[1] == '\0') + op_kind = clang::OO_Minus; + else if (post_op_name[2] == '\0') + { + switch (post_op_name[1]) + { + case '=': + op_kind = clang::OO_MinusEqual; + break; + case '-': + op_kind = clang::OO_MinusMinus; + break; + case '>': + op_kind = clang::OO_Arrow; + break; + } + } + else if (post_op_name[3] == '\0') + { + if (post_op_name[2] == '*') + op_kind = clang::OO_ArrowStar; + break; + } + break; + + case '*': + if (post_op_name[1] == '\0') + op_kind = clang::OO_Star; + else if (post_op_name[1] == '=' && post_op_name[2] == '\0') + op_kind = clang::OO_StarEqual; + break; + + case '/': + if (post_op_name[1] == '\0') + op_kind = clang::OO_Slash; + else if (post_op_name[1] == '=' && post_op_name[2] == '\0') + op_kind = clang::OO_SlashEqual; + break; + + case '%': + if (post_op_name[1] == '\0') + op_kind = clang::OO_Percent; + else if (post_op_name[1] == '=' && post_op_name[2] == '\0') + op_kind = clang::OO_PercentEqual; + break; + + case '^': + if (post_op_name[1] == '\0') + op_kind = clang::OO_Caret; + else if (post_op_name[1] == '=' && post_op_name[2] == '\0') + op_kind = clang::OO_CaretEqual; + break; + + case '&': + if (post_op_name[1] == '\0') + op_kind = clang::OO_Amp; + else if (post_op_name[2] == '\0') + { + switch (post_op_name[1]) + { + case '=': + op_kind = clang::OO_AmpEqual; + break; + case '&': + op_kind = clang::OO_AmpAmp; + break; + } + } + break; + + case '|': + if (post_op_name[1] == '\0') + op_kind = clang::OO_Pipe; + else if (post_op_name[2] == '\0') + { + switch (post_op_name[1]) + { + case '=': + op_kind = clang::OO_PipeEqual; + break; + case '|': + op_kind = clang::OO_PipePipe; + break; + } + } + break; + + case '~': + if (post_op_name[1] == '\0') + op_kind = clang::OO_Tilde; + break; + + case '!': + if (post_op_name[1] == '\0') + op_kind = clang::OO_Exclaim; + else if (post_op_name[1] == '=' && post_op_name[2] == '\0') + op_kind = clang::OO_ExclaimEqual; + break; + + case '=': + if (post_op_name[1] == '\0') + op_kind = clang::OO_Equal; + else if (post_op_name[1] == '=' && post_op_name[2] == '\0') + op_kind = clang::OO_EqualEqual; + break; + + case '<': + if (post_op_name[1] == '\0') + op_kind = clang::OO_Less; + else if (post_op_name[2] == '\0') + { + switch (post_op_name[1]) + { + case '<': + op_kind = clang::OO_LessLess; + break; + case '=': + op_kind = clang::OO_LessEqual; + break; + } + } + else if (post_op_name[3] == '\0') + { + if (post_op_name[2] == '=') + op_kind = clang::OO_LessLessEqual; + } + break; + + case '>': + if (post_op_name[1] == '\0') + op_kind = clang::OO_Greater; + else if (post_op_name[2] == '\0') + { + switch (post_op_name[1]) + { + case '>': + op_kind = clang::OO_GreaterGreater; + break; + case '=': + op_kind = clang::OO_GreaterEqual; + break; + } + } + else if (post_op_name[1] == '>' && post_op_name[2] == '=' && post_op_name[3] == '\0') + { + op_kind = clang::OO_GreaterGreaterEqual; + } + break; + + case ',': + if (post_op_name[1] == '\0') + op_kind = clang::OO_Comma; + break; + + case '(': + if (post_op_name[1] == ')' && post_op_name[2] == '\0') + op_kind = clang::OO_Call; + break; + + case '[': + if (post_op_name[1] == ']' && post_op_name[2] == '\0') + op_kind = clang::OO_Subscript; + break; + } + + return true; +} clang::AccessSpecifier ClangASTContext::ConvertAccessTypeToAccessSpecifier (AccessType access) @@ -1617,23 +1852,26 @@ } static inline bool -check_op_param (uint32_t op_kind, bool unary, bool binary, uint32_t num_params) +check_op_param(bool is_method, clang::OverloadedOperatorKind op_kind, bool unary, bool binary, uint32_t num_params) { // Special-case call since it can take any number of operands if(op_kind == OO_Call) return true; // The parameter count doesn't include "this" - if (num_params == 0) - return unary; + if (is_method) + ++num_params; if (num_params == 1) + return unary; + if (num_params == 2) return binary; else return false; } bool -ClangASTContext::CheckOverloadedOperatorKindParameterCount (uint32_t op_kind, uint32_t num_params) +ClangASTContext::CheckOverloadedOperatorKindParameterCount(bool is_method, clang::OverloadedOperatorKind op_kind, + uint32_t num_params) { switch (op_kind) { @@ -1646,8 +1884,10 @@ case OO_Array_Delete: return true; } - -#define OVERLOADED_OPERATOR(Name,Spelling,Token,Unary,Binary,MemberOnly) case OO_##Name: return check_op_param (op_kind, Unary, Binary, num_params); + +#define OVERLOADED_OPERATOR(Name, Spelling, Token, Unary, Binary, MemberOnly) \ + case OO_##Name: \ + return check_op_param(is_method, op_kind, Unary, Binary, num_params); switch (op_kind) { #include "clang/Basic/OperatorKinds.def" @@ -1997,6 +2237,35 @@ #pragma mark Function Types +clang::DeclarationName +ClangASTContext::GetDeclarationName(const char *name, const CompilerType &function_clang_type) +{ + if (!name || !name[0]) + return clang::DeclarationName(); + + clang::OverloadedOperatorKind op_kind = clang::NUM_OVERLOADED_OPERATORS; + if (!IsOperator(name, op_kind) || op_kind == clang::NUM_OVERLOADED_OPERATORS) + return DeclarationName(&getASTContext()->Idents.get(name)); // Not operator, but a regular function. + + // Check the number of operator parameters. Sometimes we have + // seen bad DWARF that doesn't correctly describe operators and + // if we try to create a method and add it to the class, clang + // will assert and crash, so we need to make sure things are + // acceptable. + clang::QualType method_qual_type(ClangASTContext::GetQualType(function_clang_type)); + const clang::FunctionProtoType *function_type = + llvm::dyn_cast<clang::FunctionProtoType>(method_qual_type.getTypePtr()); + if (function_type == nullptr) + return clang::DeclarationName(); + + const bool is_method = false; + const unsigned int num_params = function_type->getNumParams(); + if (!ClangASTContext::CheckOverloadedOperatorKindParameterCount(is_method, op_kind, num_params)) + return clang::DeclarationName(); + + return getASTContext()->DeclarationNames.getCXXOperatorName(op_kind); +} + FunctionDecl * ClangASTContext::CreateFunctionDeclaration (DeclContext *decl_ctx, const char *name, @@ -2013,34 +2282,10 @@ const bool hasWrittenPrototype = true; const bool isConstexprSpecified = false; - if (name && name[0]) - { - func_decl = FunctionDecl::Create (*ast, - decl_ctx, - SourceLocation(), - SourceLocation(), - DeclarationName (&ast->Idents.get(name)), - GetQualType(function_clang_type), - nullptr, - (clang::StorageClass)storage, - is_inline, - hasWrittenPrototype, - isConstexprSpecified); - } - else - { - func_decl = FunctionDecl::Create (*ast, - decl_ctx, - SourceLocation(), - SourceLocation(), - DeclarationName (), - GetQualType(function_clang_type), - nullptr, - (clang::StorageClass)storage, - is_inline, - hasWrittenPrototype, - isConstexprSpecified); - } + clang::DeclarationName declarationName = GetDeclarationName(name, function_clang_type); + func_decl = FunctionDecl::Create(*ast, decl_ctx, SourceLocation(), SourceLocation(), declarationName, + GetQualType(function_clang_type), nullptr, (clang::StorageClass)storage, is_inline, + hasWrittenPrototype, isConstexprSpecified); if (func_decl) decl_ctx->addDecl (func_decl); @@ -7239,222 +7484,6 @@ return CompilerType(); } -static bool -IsOperator (const char *name, clang::OverloadedOperatorKind &op_kind) -{ - if (name == nullptr || name[0] == '\0') - return false; - -#define OPERATOR_PREFIX "operator" -#define OPERATOR_PREFIX_LENGTH (sizeof (OPERATOR_PREFIX) - 1) - - const char *post_op_name = nullptr; - - bool no_space = true; - - if (::strncmp(name, OPERATOR_PREFIX, OPERATOR_PREFIX_LENGTH)) - return false; - - post_op_name = name + OPERATOR_PREFIX_LENGTH; - - if (post_op_name[0] == ' ') - { - post_op_name++; - no_space = false; - } - -#undef OPERATOR_PREFIX -#undef OPERATOR_PREFIX_LENGTH - - // This is an operator, set the overloaded operator kind to invalid - // in case this is a conversion operator... - op_kind = clang::NUM_OVERLOADED_OPERATORS; - - switch (post_op_name[0]) - { - default: - if (no_space) - return false; - break; - case 'n': - if (no_space) - return false; - if (strcmp (post_op_name, "new") == 0) - op_kind = clang::OO_New; - else if (strcmp (post_op_name, "new[]") == 0) - op_kind = clang::OO_Array_New; - break; - - case 'd': - if (no_space) - return false; - if (strcmp (post_op_name, "delete") == 0) - op_kind = clang::OO_Delete; - else if (strcmp (post_op_name, "delete[]") == 0) - op_kind = clang::OO_Array_Delete; - break; - - case '+': - if (post_op_name[1] == '\0') - op_kind = clang::OO_Plus; - else if (post_op_name[2] == '\0') - { - if (post_op_name[1] == '=') - op_kind = clang::OO_PlusEqual; - else if (post_op_name[1] == '+') - op_kind = clang::OO_PlusPlus; - } - break; - - case '-': - if (post_op_name[1] == '\0') - op_kind = clang::OO_Minus; - else if (post_op_name[2] == '\0') - { - switch (post_op_name[1]) - { - case '=': op_kind = clang::OO_MinusEqual; break; - case '-': op_kind = clang::OO_MinusMinus; break; - case '>': op_kind = clang::OO_Arrow; break; - } - } - else if (post_op_name[3] == '\0') - { - if (post_op_name[2] == '*') - op_kind = clang::OO_ArrowStar; break; - } - break; - - case '*': - if (post_op_name[1] == '\0') - op_kind = clang::OO_Star; - else if (post_op_name[1] == '=' && post_op_name[2] == '\0') - op_kind = clang::OO_StarEqual; - break; - - case '/': - if (post_op_name[1] == '\0') - op_kind = clang::OO_Slash; - else if (post_op_name[1] == '=' && post_op_name[2] == '\0') - op_kind = clang::OO_SlashEqual; - break; - - case '%': - if (post_op_name[1] == '\0') - op_kind = clang::OO_Percent; - else if (post_op_name[1] == '=' && post_op_name[2] == '\0') - op_kind = clang::OO_PercentEqual; - break; - - - case '^': - if (post_op_name[1] == '\0') - op_kind = clang::OO_Caret; - else if (post_op_name[1] == '=' && post_op_name[2] == '\0') - op_kind = clang::OO_CaretEqual; - break; - - case '&': - if (post_op_name[1] == '\0') - op_kind = clang::OO_Amp; - else if (post_op_name[2] == '\0') - { - switch (post_op_name[1]) - { - case '=': op_kind = clang::OO_AmpEqual; break; - case '&': op_kind = clang::OO_AmpAmp; break; - } - } - break; - - case '|': - if (post_op_name[1] == '\0') - op_kind = clang::OO_Pipe; - else if (post_op_name[2] == '\0') - { - switch (post_op_name[1]) - { - case '=': op_kind = clang::OO_PipeEqual; break; - case '|': op_kind = clang::OO_PipePipe; break; - } - } - break; - - case '~': - if (post_op_name[1] == '\0') - op_kind = clang::OO_Tilde; - break; - - case '!': - if (post_op_name[1] == '\0') - op_kind = clang::OO_Exclaim; - else if (post_op_name[1] == '=' && post_op_name[2] == '\0') - op_kind = clang::OO_ExclaimEqual; - break; - - case '=': - if (post_op_name[1] == '\0') - op_kind = clang::OO_Equal; - else if (post_op_name[1] == '=' && post_op_name[2] == '\0') - op_kind = clang::OO_EqualEqual; - break; - - case '<': - if (post_op_name[1] == '\0') - op_kind = clang::OO_Less; - else if (post_op_name[2] == '\0') - { - switch (post_op_name[1]) - { - case '<': op_kind = clang::OO_LessLess; break; - case '=': op_kind = clang::OO_LessEqual; break; - } - } - else if (post_op_name[3] == '\0') - { - if (post_op_name[2] == '=') - op_kind = clang::OO_LessLessEqual; - } - break; - - case '>': - if (post_op_name[1] == '\0') - op_kind = clang::OO_Greater; - else if (post_op_name[2] == '\0') - { - switch (post_op_name[1]) - { - case '>': op_kind = clang::OO_GreaterGreater; break; - case '=': op_kind = clang::OO_GreaterEqual; break; - } - } - else if (post_op_name[1] == '>' && - post_op_name[2] == '=' && - post_op_name[3] == '\0') - { - op_kind = clang::OO_GreaterGreaterEqual; - } - break; - - case ',': - if (post_op_name[1] == '\0') - op_kind = clang::OO_Comma; - break; - - case '(': - if (post_op_name[1] == ')' && post_op_name[2] == '\0') - op_kind = clang::OO_Call; - break; - - case '[': - if (post_op_name[1] == ']' && post_op_name[2] == '\0') - op_kind = clang::OO_Subscript; - break; - } - - return true; -} - clang::EnumDecl * ClangASTContext::GetAsEnumDecl (const CompilerType& type) { @@ -7846,7 +7875,8 @@ // if we try to create a method and add it to the class, clang // will assert and crash, so we need to make sure things are // acceptable. - if (!ClangASTContext::CheckOverloadedOperatorKindParameterCount (op_kind, num_params)) + const bool is_method = true; + if (!ClangASTContext::CheckOverloadedOperatorKindParameterCount(is_method, op_kind, num_params)) return nullptr; cxx_method_decl = clang::CXXMethodDecl::Create (*getASTContext(), cxx_record_decl, Index: source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp =================================================================== --- source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp +++ source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp @@ -2051,13 +2051,10 @@ StreamString ss; function->DumpSymbolContext(&ss); - + log->Printf(" CEDM::FEVD[%u] Imported decl for function %s (description %s), returned %s", - current_id, - copied_function_decl->getName().str().c_str(), - ss.GetData(), + current_id, copied_function_decl->getNameAsString().c_str(), ss.GetData(), ast_dumper.GetCString()); - } context.AddNamedDecl(copied_function_decl); Index: packages/Python/lldbsuite/test/lang/cpp/global_operators/main.cpp =================================================================== --- packages/Python/lldbsuite/test/lang/cpp/global_operators/main.cpp +++ packages/Python/lldbsuite/test/lang/cpp/global_operators/main.cpp @@ -1,11 +1,37 @@ +#include <new> + +struct new_tag_t +{ +}; +new_tag_t new_tag; + struct Struct { int value; }; bool operator==(const Struct &a, const Struct &b) { return a.value == b.value; } +typedef char buf_t[sizeof(Struct)]; +buf_t global_new_buf, tagged_new_buf; + +// This overrides global operator new +// This function and the following does not actually allocate memory. We are merely +// trying to make sure it is getting called. +void * +operator new(std::size_t count) +{ + return &global_new_buf; +} + +// A custom allocator +void * +operator new(std::size_t count, const new_tag_t &) +{ + return &tagged_new_buf; +} + int main() { Struct s1, s2, s3; s1.value = 3; Index: packages/Python/lldbsuite/test/lang/cpp/global_operators/TestCppGlobalOperators.py =================================================================== --- packages/Python/lldbsuite/test/lang/cpp/global_operators/TestCppGlobalOperators.py +++ packages/Python/lldbsuite/test/lang/cpp/global_operators/TestCppGlobalOperators.py @@ -10,8 +10,7 @@ mydir = TestBase.compute_mydir(__file__) - @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr21765") - def test_with_run_command(self): + def prepare_executable_and_get_frame(self): self.build() # Get main source file @@ -42,8 +41,11 @@ self.assertTrue(process.GetState() == lldb.eStateStopped, PROCESS_STOPPED) thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) - # Check if global operators are evaluated - frame = thread.GetSelectedFrame() + return thread.GetSelectedFrame() + + @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr21765") + def test_equals_operator(self): + frame = self.prepare_executable_and_get_frame() test_result = frame.EvaluateExpression("operator==(s1, s2)") self.assertTrue(test_result.IsValid() and test_result.GetValue() == "false", "operator==(s1, s2) = false") @@ -53,3 +55,25 @@ test_result = frame.EvaluateExpression("operator==(s2, s3)") self.assertTrue(test_result.IsValid() and test_result.GetValue() == "false", "operator==(s2, s3) = false") + + def do_new_test(self, frame, expr, expected_value_name): + """Evaluate a new expression, and check its result""" + + expected_value = frame.FindValue(expected_value_name, lldb.eValueTypeVariableGlobal) + self.assertTrue(expected_value.IsValid()) + + expected_value_addr = expected_value.AddressOf() + self.assertTrue(expected_value_addr.IsValid()) + + got = frame.EvaluateExpression(expr) + self.assertTrue(got.IsValid()) + self.assertEqual(got.GetValueAsUnsigned(), expected_value_addr.GetValueAsUnsigned()) + got_type = got.GetType() + self.assertTrue(got_type.IsPointerType()) + self.assertEqual(got_type.GetPointeeType().GetName(), "Struct") + + def test_operator_new(self): + frame = self.prepare_executable_and_get_frame() + + self.do_new_test(frame, "new Struct()", "global_new_buf") + self.do_new_test(frame, "new(new_tag) Struct()", "tagged_new_buf") Index: include/lldb/Symbol/ClangASTContext.h =================================================================== --- include/lldb/Symbol/ClangASTContext.h +++ include/lldb/Symbol/ClangASTContext.h @@ -382,10 +382,9 @@ static clang::DeclContext * GetAsDeclContext (clang::ObjCMethodDecl *objc_method_decl); - static bool - CheckOverloadedOperatorKindParameterCount (uint32_t op_kind, - uint32_t num_params); + CheckOverloadedOperatorKindParameterCount(bool is_method, clang::OverloadedOperatorKind op_kind, + uint32_t num_params); bool FieldIsBitfield (clang::FieldDecl* field, @@ -1209,6 +1208,9 @@ return clang::QualType(); } + clang::DeclarationName + GetDeclarationName(const char *name, const CompilerType &function_clang_type); + //------------------------------------------------------------------ // Classes that inherit from ClangASTContext can see and modify these //------------------------------------------------------------------
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits