diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h --- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h +++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h @@ -315,8 +315,7 @@ Status SetBreakpointCommandCallbackFunction( std::vector &bp_options_vec, - const char *function_name, - StructuredData::ObjectSP extra_args_sp); + const char *function_name, StructuredData::ObjectSP extra_args_sp); /// Set a script function as the callback for the breakpoint. virtual Status @@ -472,9 +471,9 @@ const char *GetScriptInterpreterPtyName(); int GetMasterFileDescriptor(); - - virtual llvm::Expected - GetNumFixedArgumentsForCallable(const llvm::StringRef &callable_name) { + + virtual llvm::Expected + GetMaxPositionalArgumentsForCallable(const llvm::StringRef &callable_name) { return llvm::createStringError( llvm::inconvertibleErrorCode(), "Unimplemented function"); } diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestBreakpointCommandsFromPython.py b/lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestBreakpointCommandsFromPython.py --- a/lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestBreakpointCommandsFromPython.py +++ b/lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestBreakpointCommandsFromPython.py @@ -132,7 +132,7 @@ # Now finish, and make sure the return value is correct. threads = lldbutil.get_threads_stopped_at_breakpoint( self.process, body_bkpt) - self.assertTrue(len(threads) == 1, "Stopped at inner breakpoint.") + self.assertEquals(len(threads), 1, "Stopped at inner breakpoint.") self.thread = threads[0] self.assertEquals("callback was here", side_effect.callback) diff --git a/lldb/scripts/Python/python-wrapper.swig b/lldb/scripts/Python/python-wrapper.swig --- a/lldb/scripts/Python/python-wrapper.swig +++ b/lldb/scripts/Python/python-wrapper.swig @@ -39,7 +39,7 @@ // This function is called by lldb_private::ScriptInterpreterPython::BreakpointCallbackFunction(...) // and is used when a script command is attached to a breakpoint for execution. -SWIGEXPORT bool +SWIGEXPORT llvm::Expected LLDBSwigPythonBreakpointCallbackFunction ( const char *python_function_name, @@ -49,38 +49,40 @@ lldb_private::StructuredDataImpl *args_impl ) { + using namespace llvm; + lldb::SBFrame sb_frame (frame_sp); lldb::SBBreakpointLocation sb_bp_loc(bp_loc_sp); - bool stop_at_breakpoint = true; - PyErr_Cleaner py_err_cleaner(true); auto dict = PythonModule::MainModule().ResolveName(session_dictionary_name); auto pfunc = PythonObject::ResolveNameWithDictionary(python_function_name, dict); - if (!pfunc.IsAllocated()) - return stop_at_breakpoint; + unsigned max_positional_args; + if (auto arg_info = pfunc.GetArgInfo()) + max_positional_args = arg_info.get().max_positional_args; + else + return arg_info.takeError(); PythonObject frame_arg(PyRefType::Owned, SBTypeToSWIGWrapper(sb_frame)); PythonObject bp_loc_arg(PyRefType::Owned, SBTypeToSWIGWrapper(sb_bp_loc)); - PythonObject result; - // If the called function doesn't take extra_args, drop them here: - auto arg_info = pfunc.GetNumArguments(); - if (arg_info.count == 3) - result = pfunc(frame_arg, bp_loc_arg, dict); - else if (arg_info.count == 4) { - lldb::SBStructuredData *args_value = new lldb::SBStructuredData(args_impl); - PythonObject args_arg(PyRefType::Owned, SBTypeToSWIGWrapper(args_value)); - result = pfunc(frame_arg, bp_loc_arg, args_arg, dict); - } else { - return stop_at_breakpoint; - } + auto result = [&] () -> Expected { + // If the called function doesn't take extra_args, drop them here: + if (max_positional_args < 4) { + return pfunc.Call(frame_arg, bp_loc_arg, dict); + } else { + lldb::SBStructuredData *args_value = new lldb::SBStructuredData(args_impl); + PythonObject args_arg(PyRefType::Owned, SBTypeToSWIGWrapper(args_value)); + return pfunc.Call(frame_arg, bp_loc_arg, args_arg, dict); + } + } (); - if (result.get() == Py_False) - stop_at_breakpoint = false; + if (!result) + return result.takeError(); - return stop_at_breakpoint; + // Only False counts as false! + return result.get().get() != Py_False; } // This function is called by lldb_private::ScriptInterpreterPython::WatchpointCallbackFunction(...) diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp @@ -72,11 +72,16 @@ // These prototypes are the Pythonic implementations of the required callbacks. // Although these are scripting-language specific, their definition depends on // the public API. -extern "C" bool LLDBSwigPythonBreakpointCallbackFunction( + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreturn-type-c-linkage" + +extern "C" llvm::Expected LLDBSwigPythonBreakpointCallbackFunction( const char *python_function_name, const char *session_dictionary_name, const lldb::StackFrameSP &sb_frame, - const lldb::BreakpointLocationSP &sb_bp_loc, - StructuredDataImpl *args_impl); + const lldb::BreakpointLocationSP &sb_bp_loc, StructuredDataImpl *args_impl); + +#pragma clang diagnostic pop extern "C" bool LLDBSwigPythonWatchpointCallbackFunction( const char *python_function_name, const char *session_dictionary_name, @@ -782,10 +787,9 @@ return m_sys_module_dict; } -llvm::Expected -ScriptInterpreterPythonImpl::GetNumFixedArgumentsForCallable( - const llvm::StringRef &callable_name) -{ +llvm::Expected +ScriptInterpreterPythonImpl::GetMaxPositionalArgumentsForCallable( + const llvm::StringRef &callable_name) { if (callable_name.empty()) { return llvm::createStringError( llvm::inconvertibleErrorCode(), @@ -796,16 +800,15 @@ Locker::NoSTDIN); auto dict = PythonModule::MainModule() .ResolveName(m_dictionary_name); - auto pfunc - = PythonObject::ResolveNameWithDictionary(callable_name, - dict); + auto pfunc = PythonObject::ResolveNameWithDictionary( + callable_name, dict); if (!pfunc.IsAllocated()) { return llvm::createStringError( llvm::inconvertibleErrorCode(), "can't find callable: %s", callable_name.str().c_str()); } PythonCallable::ArgInfo arg_info = pfunc.GetNumArguments(); - return arg_info.count; + return arg_info.max_positional_args; } static std::string GenerateUniqueName(const char *base_name_wanted, @@ -1232,22 +1235,22 @@ // For now just cons up a oneliner that calls the provided function. std::string oneliner("return "); oneliner += function_name; - - llvm::Expected maybe_args - = GetNumFixedArgumentsForCallable(function_name); + + llvm::Expected maybe_args = + GetMaxPositionalArgumentsForCallable(function_name); if (!maybe_args) { - error.SetErrorStringWithFormat("could not get num args: %s", + error.SetErrorStringWithFormat( + "could not get num args: %s", llvm::toString(maybe_args.takeError()).c_str()); return error; } - size_t num_args = *maybe_args; - + size_t max_args = *maybe_args; + bool uses_extra_args = false; - if (num_args == 4) { + if (max_args >= 4) { uses_extra_args = true; oneliner += "(frame, bp_loc, extra_args, internal_dict)"; - } - else if (num_args == 3) { + } else if (max_args >= 3) { if (extra_args_sp) { error.SetErrorString("cannot pass extra_args to a three argument callback" ); @@ -1257,12 +1260,12 @@ oneliner += "(frame, bp_loc, internal_dict)"; } else { error.SetErrorStringWithFormat("expected 3 or 4 argument " - "function, %s has %zu", - function_name, num_args); + "function, %s can only take %zu", + function_name, max_args); return error; } - - SetBreakpointCommandCallback(bp_options, oneliner.c_str(), extra_args_sp, + + SetBreakpointCommandCallback(bp_options, oneliner.c_str(), extra_args_sp, uses_extra_args); return error; } @@ -1840,8 +1843,7 @@ StructuredData::ObjectSP ScriptInterpreterPythonImpl::CreateScriptedThreadPlan( const char *class_name, StructuredDataImpl *args_data, - std::string &error_str, - lldb::ThreadPlanSP thread_plan_sp) { + std::string &error_str, lldb::ThreadPlanSP thread_plan_sp) { if (class_name == nullptr || class_name[0] == '\0') return StructuredData::ObjectSP(); @@ -2145,7 +2147,7 @@ std::string auto_generated_function_name(GenerateUniqueName( "lldb_autogen_python_bp_callback_func_", num_created_functions)); - if (has_extra_args) + if (has_extra_args) sstr.Printf("def %s (frame, bp_loc, extra_args, internal_dict):", auto_generated_function_name.c_str()); else @@ -2267,11 +2269,26 @@ Locker py_lock(python_interpreter, Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); - ret_val = LLDBSwigPythonBreakpointCallbackFunction( - python_function_name, - python_interpreter->m_dictionary_name.c_str(), stop_frame_sp, - bp_loc_sp, - bp_option_data->m_extra_args_up.get()); + Expected maybe_ret_val = + LLDBSwigPythonBreakpointCallbackFunction( + python_function_name, + python_interpreter->m_dictionary_name.c_str(), stop_frame_sp, + bp_loc_sp, bp_option_data->m_extra_args_up.get()); + + if (!maybe_ret_val) { + + llvm::handleAllErrors( + maybe_ret_val.takeError(), + [&](PythonException &E) { + debugger.GetErrorStream() << E.ReadBacktrace(); + }, + [&](const llvm::ErrorInfoBase &E) { + debugger.GetErrorStream() << E.message(); + }); + + } else { + ret_val = maybe_ret_val.get(); + } } return ret_val; } diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h @@ -256,11 +256,10 @@ BreakpointOptions *bp_options, std::unique_ptr &data_up) override; - Status SetBreakpointCommandCallback( - BreakpointOptions *bp_options, - const char *command_body_text, - StructuredData::ObjectSP extra_args_sp, - bool uses_extra_args); + Status SetBreakpointCommandCallback(BreakpointOptions *bp_options, + const char *command_body_text, + StructuredData::ObjectSP extra_args_sp, + bool uses_extra_args); /// Set a one-liner as the callback for the watchpoint. void SetWatchpointCommandCallback(WatchpointOptions *wp_options, @@ -378,10 +377,9 @@ python::PythonDictionary &GetSessionDictionary(); python::PythonDictionary &GetSysModuleDictionary(); - - llvm::Expected - GetNumFixedArgumentsForCallable(const llvm::StringRef &callable_name) - override; + + llvm::Expected GetMaxPositionalArgumentsForCallable( + const llvm::StringRef &callable_name) override; bool GetEmbeddedInterpreterModuleObjects(); diff --git a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp --- a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp +++ b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp @@ -59,7 +59,7 @@ #define LLDBSwigPyInit init_lldb #endif -extern "C" bool LLDBSwigPythonBreakpointCallbackFunction( +extern "C" llvm::Expected LLDBSwigPythonBreakpointCallbackFunction( const char *python_function_name, const char *session_dictionary_name, const lldb::StackFrameSP &sb_frame, const lldb::BreakpointLocationSP &sb_bp_loc,