jingham updated this revision to Diff 224929.
jingham added a comment.

Addressed Pavel's comments.

I explicitly want the number of fixed arguments, so I changed the function name 
to GetNumFixedArguments.  The docs say explicitly that you have to make the 
function take either three or four fixed arguments, and that's what I have to 
check against.  I don't want to know about varargs and optional named arguments.

I added the Expected, though I didn't plumb the change into 
PythonCallable::GetNumArguments.  That's of marginal extra benefit, and 
orthogonal to the purposes of this patch.

Note also, the fact that I was now checking argument signatures when adding the 
command means you can no longer do:

  (lldb) break command add -F myfunc.function
  (lldb) command script import myfunc.py

which TestBreakpointCommand.py was doing.  You have to make the Python function 
available BEFORE adding it to the breakpoint.  I don't think this was an 
feature, and I don't see any harm in adding that requirement.  But it is a 
change in behavior so I thought I'd bring it up.


Repository:
  rLLDB LLDB

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D68671/new/

https://reviews.llvm.org/D68671

Files:
  lldb/include/lldb/API/SBBreakpoint.h
  lldb/include/lldb/API/SBBreakpointLocation.h
  lldb/include/lldb/API/SBBreakpointName.h
  lldb/include/lldb/API/SBStructuredData.h
  lldb/include/lldb/Interpreter/OptionGroupPythonClassWithDict.h
  lldb/include/lldb/Interpreter/ScriptInterpreter.h
  
lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestBreakpointCommand.py
  
lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestBreakpointCommandsFromPython.py
  
lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/bktptcmd.py
  lldb/scripts/Python/python-wrapper.swig
  lldb/scripts/interface/SBBreakpoint.i
  lldb/scripts/interface/SBBreakpointLocation.i
  lldb/scripts/interface/SBBreakpointName.i
  lldb/source/API/SBBreakpoint.cpp
  lldb/source/API/SBBreakpointLocation.cpp
  lldb/source/API/SBBreakpointName.cpp
  lldb/source/Commands/CommandObjectBreakpoint.cpp
  lldb/source/Commands/CommandObjectBreakpointCommand.cpp
  lldb/source/Commands/CommandObjectThread.cpp
  lldb/source/Commands/Options.td
  lldb/source/Interpreter/OptionGroupPythonClassWithDict.cpp
  lldb/source/Interpreter/ScriptInterpreter.cpp
  lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
  lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h
  lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
  lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp

Index: lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
===================================================================
--- lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
+++ lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
@@ -62,7 +62,8 @@
 extern "C" bool LLDBSwigPythonBreakpointCallbackFunction(
     const char *python_function_name, const char *session_dictionary_name,
     const lldb::StackFrameSP &sb_frame,
-    const lldb::BreakpointLocationSP &sb_bp_loc) {
+    const lldb::BreakpointLocationSP &sb_bp_loc,
+    StructuredDataImpl *args_impl) {
   return false;
 }
 
Index: lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
===================================================================
--- lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
+++ lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
@@ -179,8 +179,10 @@
   Status GenerateFunction(const char *signature,
                           const StringList &input) override;
 
-  Status GenerateBreakpointCommandCallbackData(StringList &input,
-                                               std::string &output) override;
+  Status GenerateBreakpointCommandCallbackData(
+      StringList &input,
+      std::string &output,
+      bool has_extra_args) override;
 
   bool GenerateWatchpointCommandCallbackData(StringList &input,
                                              std::string &output) override;
@@ -244,14 +246,22 @@
   Status SetBreakpointCommandCallback(BreakpointOptions *bp_options,
                                       const char *callback_body) override;
 
-  void SetBreakpointCommandCallbackFunction(BreakpointOptions *bp_options,
-                                            const char *function_name) override;
+  Status SetBreakpointCommandCallbackFunction(
+      BreakpointOptions *bp_options,
+      const char *function_name,
+      StructuredData::ObjectSP extra_args_sp) override;
 
   /// This one is for deserialization:
   Status SetBreakpointCommandCallback(
       BreakpointOptions *bp_options,
       std::unique_ptr<BreakpointOptions::CommandData> &data_up) override;
 
+  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,
                                     const char *oneliner) override;
@@ -366,6 +376,10 @@
   PythonDictionary &GetSessionDictionary();
 
   PythonDictionary &GetSysModuleDictionary();
+  
+  llvm::Expected<size_t> 
+  GetNumFixedArgumentsForCallable(const llvm::StringRef &callable_name) 
+      override;
 
   bool GetEmbeddedInterpreterModuleObjects();
 
Index: lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h
===================================================================
--- lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h
+++ lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h
@@ -17,6 +17,7 @@
 
 #include "lldb/Breakpoint/BreakpointOptions.h"
 #include "lldb/Core/IOHandler.h"
+#include "lldb/Core/StructuredDataImpl.h"
 #include "lldb/Interpreter/ScriptInterpreter.h"
 #include "lldb/lldb-private.h"
 
@@ -34,6 +35,13 @@
     CommandDataPython() : BreakpointOptions::CommandData() {
       interpreter = lldb::eScriptLanguagePython;
     }
+    CommandDataPython(StructuredData::ObjectSP extra_args_sp) :
+        BreakpointOptions::CommandData(),
+        m_extra_args_up(new StructuredDataImpl()) {
+        interpreter = lldb::eScriptLanguagePython;
+        m_extra_args_up->SetObjectSP(extra_args_sp);
+    }
+    lldb::StructuredDataImplUP m_extra_args_up;
   };
 
   ScriptInterpreterPython(Debugger &debugger)
Index: lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
===================================================================
--- lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
+++ lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
@@ -74,7 +74,8 @@
 extern "C" bool LLDBSwigPythonBreakpointCallbackFunction(
     const char *python_function_name, const char *session_dictionary_name,
     const lldb::StackFrameSP &sb_frame,
-    const lldb::BreakpointLocationSP &sb_bp_loc);
+    const lldb::BreakpointLocationSP &sb_bp_loc,
+    StructuredDataImpl *args_impl);
 
 extern "C" bool LLDBSwigPythonWatchpointCallbackFunction(
     const char *python_function_name, const char *session_dictionary_name,
@@ -551,8 +552,10 @@
         break;
       data_up->user_source.SplitIntoLines(data);
 
+      StructuredData::ObjectSP empty_args_sp;
       if (GenerateBreakpointCommandCallbackData(data_up->user_source,
-                                                data_up->script_source)
+                                                data_up->script_source,
+                                                false)
               .Success()) {
         auto baton_sp = std::make_shared<BreakpointOptions::CommandBaton>(
             std::move(data_up));
@@ -777,6 +780,32 @@
   return m_sys_module_dict;
 }
 
+llvm::Expected<size_t>
+ScriptInterpreterPythonImpl::GetNumFixedArgumentsForCallable(
+    const llvm::StringRef &callable_name)
+{
+  if (callable_name.empty()) {
+    return llvm::createStringError(
+        llvm::inconvertibleErrorCode(),
+        "called with empty callable name.");
+  }
+  Locker py_lock(this, Locker::AcquireLock |
+                 Locker::InitSession |
+                 Locker::NoSTDIN);
+  auto dict = PythonModule::MainModule()
+      .ResolveName<PythonDictionary>(m_dictionary_name);
+  auto pfunc 
+     = PythonObject::ResolveNameWithDictionary<PythonCallable>(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;
+}
+
 static std::string GenerateUniqueName(const char *base_name_wanted,
                                       uint32_t &functions_counter,
                                       const void *name_token = nullptr) {
@@ -1258,14 +1287,46 @@
       "    ", *this, true, wp_options);
 }
 
-void ScriptInterpreterPythonImpl::SetBreakpointCommandCallbackFunction(
-    BreakpointOptions *bp_options, const char *function_name) {
+Status ScriptInterpreterPythonImpl::SetBreakpointCommandCallbackFunction(
+    BreakpointOptions *bp_options, const char *function_name,
+    StructuredData::ObjectSP extra_args_sp) {
+  Status error;
   // For now just cons up a oneliner that calls the provided function.
   std::string oneliner("return ");
   oneliner += function_name;
-  oneliner += "(frame, bp_loc, internal_dict)";
-  m_debugger.GetScriptInterpreter()->SetBreakpointCommandCallback(
-      bp_options, oneliner.c_str());
+  
+  llvm::Expected<size_t> maybe_args 
+      = GetNumFixedArgumentsForCallable(function_name);
+  if (!maybe_args) {
+    error.SetErrorStringWithFormat("could not get num args: %s", 
+        llvm::toString(maybe_args.takeError()).c_str());
+    return error;
+  }
+  size_t num_args = *maybe_args;
+  
+  bool uses_extra_args = false;
+  if (num_args == 4) {
+    uses_extra_args = true;
+    oneliner += "(frame, bp_loc, extra_args, internal_dict)";
+  }
+  else if (num_args == 3) {
+    if (extra_args_sp) {
+      error.SetErrorString("cannot pass extra_args to a three argument callback"
+                          );
+      return error;
+    }
+    uses_extra_args = false;
+    oneliner += "(frame, bp_loc, internal_dict)";
+  } else {
+    error.SetErrorStringWithFormat("expected 3 or 4 argument "
+                                   "function, %s has %d",
+                                   function_name, num_args);
+    return error;
+  }
+  
+  SetBreakpointCommandCallback(bp_options, oneliner.c_str(), extra_args_sp, 
+                               uses_extra_args);
+  return error;
 }
 
 Status ScriptInterpreterPythonImpl::SetBreakpointCommandCallback(
@@ -1273,7 +1334,8 @@
     std::unique_ptr<BreakpointOptions::CommandData> &cmd_data_up) {
   Status error;
   error = GenerateBreakpointCommandCallbackData(cmd_data_up->user_source,
-                                                cmd_data_up->script_source);
+                                                cmd_data_up->script_source,
+                                                false);
   if (error.Fail()) {
     return error;
   }
@@ -1284,11 +1346,17 @@
   return error;
 }
 
-// Set a Python one-liner as the callback for the breakpoint.
 Status ScriptInterpreterPythonImpl::SetBreakpointCommandCallback(
     BreakpointOptions *bp_options, const char *command_body_text) {
-  auto data_up = std::make_unique<CommandDataPython>();
+  return SetBreakpointCommandCallback(bp_options, command_body_text, {},false);
+}
 
+// Set a Python one-liner as the callback for the breakpoint.
+Status ScriptInterpreterPythonImpl::SetBreakpointCommandCallback(
+    BreakpointOptions *bp_options, const char *command_body_text,
+    StructuredData::ObjectSP extra_args_sp,
+    bool uses_extra_args) {
+  auto data_up = std::make_unique<CommandDataPython>(extra_args_sp);
   // Split the command_body_text into lines, and pass that to
   // GenerateBreakpointCommandCallbackData.  That will wrap the body in an
   // auto-generated function, and return the function name in script_source.
@@ -1296,7 +1364,8 @@
 
   data_up->user_source.SplitIntoLines(command_body_text);
   Status error = GenerateBreakpointCommandCallbackData(data_up->user_source,
-                                                       data_up->script_source);
+                                                       data_up->script_source,
+                                                       uses_extra_args);
   if (error.Success()) {
     auto baton_sp =
         std::make_shared<BreakpointOptions::CommandBaton>(std::move(data_up));
@@ -2118,7 +2187,8 @@
 }
 
 Status ScriptInterpreterPythonImpl::GenerateBreakpointCommandCallbackData(
-    StringList &user_input, std::string &output) {
+    StringList &user_input, std::string &output,
+    bool has_extra_args) {
   static uint32_t num_created_functions = 0;
   user_input.RemoveBlankLines();
   StreamString sstr;
@@ -2130,8 +2200,12 @@
 
   std::string auto_generated_function_name(GenerateUniqueName(
       "lldb_autogen_python_bp_callback_func_", num_created_functions));
-  sstr.Printf("def %s (frame, bp_loc, internal_dict):",
-              auto_generated_function_name.c_str());
+  if (has_extra_args) 
+    sstr.Printf("def %s (frame, bp_loc, extra_args, internal_dict):",
+                auto_generated_function_name.c_str());
+  else
+    sstr.Printf("def %s (frame, bp_loc, internal_dict):",
+                auto_generated_function_name.c_str());
 
   error = GenerateFunction(sstr.GetData(), user_input);
   if (!error.Success())
@@ -2251,7 +2325,8 @@
           ret_val = LLDBSwigPythonBreakpointCallbackFunction(
               python_function_name,
               python_interpreter->m_dictionary_name.c_str(), stop_frame_sp,
-              bp_loc_sp);
+              bp_loc_sp,
+              bp_option_data->m_extra_args_up.get());
         }
         return ret_val;
       }
Index: lldb/source/Interpreter/ScriptInterpreter.cpp
===================================================================
--- lldb/source/Interpreter/ScriptInterpreter.cpp
+++ lldb/source/Interpreter/ScriptInterpreter.cpp
@@ -81,12 +81,18 @@
   return return_error;
 }
 
-void ScriptInterpreter::SetBreakpointCommandCallbackFunction(
+Status ScriptInterpreter::SetBreakpointCommandCallbackFunction(
     std::vector<BreakpointOptions *> &bp_options_vec,
-    const char *function_name) {
+    const char *function_name,
+    StructuredData::ObjectSP extra_args_sp) {
+  Status error;
   for (BreakpointOptions *bp_options : bp_options_vec) {
-    SetBreakpointCommandCallbackFunction(bp_options, function_name);
+    error = SetBreakpointCommandCallbackFunction(bp_options, function_name, 
+                                         extra_args_sp);
+    if (!error.Success())
+      return error;
   }
+  return error;
 }
 
 std::unique_ptr<ScriptInterpreterLocker>
Index: lldb/source/Interpreter/OptionGroupPythonClassWithDict.cpp
===================================================================
--- lldb/source/Interpreter/OptionGroupPythonClassWithDict.cpp
+++ lldb/source/Interpreter/OptionGroupPythonClassWithDict.cpp
@@ -15,30 +15,29 @@
 
 OptionGroupPythonClassWithDict::OptionGroupPythonClassWithDict
     (const char *class_use,
+     bool is_class,
      int class_option,
      int key_option, 
-     int value_option,
-     const char *class_long_option,
-     const char *key_long_option,
-     const char *value_long_option,
-     bool required) {
-  m_key_usage_text.assign("The key for a key/value pair passed to the class"
-                            " that implements a ");
+     int value_option) : OptionGroup(), m_is_class(is_class) {
+  m_key_usage_text.assign("The key for a key/value pair passed to the "
+                          "implementation of a ");
   m_key_usage_text.append(class_use);
   m_key_usage_text.append(".  Pairs can be specified more than once.");
   
-  m_value_usage_text.assign("The value for a previous key in the pair passed to"
-                            " the class that implements a ");
+  m_value_usage_text.assign("The value for the previous key in the pair passed "
+                            "to the implementation of a ");
   m_value_usage_text.append(class_use);
   m_value_usage_text.append(".  Pairs can be specified more than once.");
   
-  m_class_usage_text.assign("The name of the class that will manage a ");
+  m_class_usage_text.assign("The name of the ");
+  m_class_usage_text.append(m_is_class ? "class" : "function");
+  m_class_usage_text.append(" that will manage a ");
   m_class_usage_text.append(class_use);
   m_class_usage_text.append(".");
   
   m_option_definition[0].usage_mask = LLDB_OPT_SET_1;
-  m_option_definition[0].required = required;
-  m_option_definition[0].long_option = class_long_option;
+  m_option_definition[0].required = true;
+  m_option_definition[0].long_option = "script-class";
   m_option_definition[0].short_option = class_option;
   m_option_definition[0].validator = nullptr;
   m_option_definition[0].option_has_arg = OptionParser::eRequiredArgument;
@@ -47,9 +46,9 @@
   m_option_definition[0].argument_type = eArgTypePythonClass;
   m_option_definition[0].usage_text = m_class_usage_text.data();
 
-  m_option_definition[1].usage_mask = LLDB_OPT_SET_1;
-  m_option_definition[1].required = required;
-  m_option_definition[1].long_option = key_long_option;
+  m_option_definition[1].usage_mask = LLDB_OPT_SET_2;
+  m_option_definition[1].required = false;
+  m_option_definition[1].long_option = "structured-data-key";
   m_option_definition[1].short_option = key_option;
   m_option_definition[1].validator = nullptr;
   m_option_definition[1].option_has_arg = OptionParser::eRequiredArgument;
@@ -58,9 +57,9 @@
   m_option_definition[1].argument_type = eArgTypeNone;
   m_option_definition[1].usage_text = m_key_usage_text.data();
 
-  m_option_definition[2].usage_mask = LLDB_OPT_SET_1;
-  m_option_definition[2].required = required;
-  m_option_definition[2].long_option = value_long_option;
+  m_option_definition[2].usage_mask = LLDB_OPT_SET_2;
+  m_option_definition[2].required = false;
+  m_option_definition[2].long_option = "structured-data-value";
   m_option_definition[2].short_option = value_option;
   m_option_definition[2].validator = nullptr;
   m_option_definition[2].option_has_arg = OptionParser::eRequiredArgument;
@@ -68,6 +67,18 @@
   m_option_definition[2].completion_type = 0;
   m_option_definition[2].argument_type = eArgTypeNone;
   m_option_definition[2].usage_text = m_value_usage_text.data();
+  
+  m_option_definition[3].usage_mask = LLDB_OPT_SET_3;
+  m_option_definition[3].required = true;
+  m_option_definition[3].long_option = "python-function";
+  m_option_definition[3].short_option = class_option;
+  m_option_definition[3].validator = nullptr;
+  m_option_definition[3].option_has_arg = OptionParser::eRequiredArgument;
+  m_option_definition[3].enum_values = {};
+  m_option_definition[3].completion_type = 0;
+  m_option_definition[3].argument_type = eArgTypePythonFunction;
+  m_option_definition[3].usage_text = m_class_usage_text.data();
+
 }
 
 OptionGroupPythonClassWithDict::~OptionGroupPythonClassWithDict() {}
@@ -78,10 +89,13 @@
     ExecutionContext *execution_context) {
   Status error;
   switch (option_idx) {
-  case 0: {
-    m_class_name.assign(option_arg);
+  case 0:
+  case 3: {
+    m_name.assign(option_arg);
   } break;
   case 1: {
+      if (!m_dict_sp)
+        m_dict_sp = std::make_shared<StructuredData::Dictionary>();
       if (m_current_key.empty())
         m_current_key.assign(option_arg);
       else
@@ -90,6 +104,8 @@
     
   } break;
   case 2: {
+      if (!m_dict_sp)
+        m_dict_sp = std::make_shared<StructuredData::Dictionary>();
       if (!m_current_key.empty()) {
           m_dict_sp->AddStringItem(m_current_key, option_arg);
           m_current_key.clear();
@@ -107,7 +123,10 @@
 void OptionGroupPythonClassWithDict::OptionParsingStarting(
   ExecutionContext *execution_context) {
   m_current_key.erase();
-  m_dict_sp = std::make_shared<StructuredData::Dictionary>();
+  // Leave the dictionary shared pointer unset.  That way you can tell that
+  // the user didn't pass any -k -v pairs.  We want to be able to warn if these
+  // were passed when the function they passed won't use them.
+  m_dict_sp.reset();
 }
 
 Status OptionGroupPythonClassWithDict::OptionParsingFinished(
Index: lldb/source/Commands/Options.td
===================================================================
--- lldb/source/Commands/Options.td
+++ lldb/source/Commands/Options.td
@@ -268,10 +268,6 @@
     EnumArg<"None", "ScriptOptionEnum()">,
     Desc<"Specify the language for the commands - if none is specified, the "
     "lldb command interpreter will be used.">;
-  def breakpoint_add_python_function : Option<"python-function", "F">,
-    Group<2>, Arg<"PythonFunction">,
-    Desc<"Give the name of a Python function to run as command for this "
-    "breakpoint. Be sure to give a module name if appropriate.">;
   def breakpoint_add_dummy_breakpoints : Option<"dummy-breakpoints", "D">,
     Desc<"Sets Dummy breakpoints - i.e. breakpoints set before a file is "
     "provided, which prime new targets.">;
@@ -907,9 +903,6 @@
   def thread_step_scope_step_in_target : Option<"step-in-target", "t">,
     Group<1>, Arg<"FunctionName">, Desc<"The name of the directly called "
     "function step in should stop at when stepping into.">;
-  def thread_step_scope_python_class : Option<"python-class", "C">, Group<2>,
-    Arg<"PythonClass">, Desc<"The name of the class that will manage this step "
-    "- only supported for Scripted Step.">;
 }
 
 let Command = "thread until" in {
Index: lldb/source/Commands/CommandObjectThread.cpp
===================================================================
--- lldb/source/Commands/CommandObjectThread.cpp
+++ lldb/source/Commands/CommandObjectThread.cpp
@@ -546,7 +546,9 @@
     m_arguments.push_back(arg);
     
     if (step_type == eStepTypeScripted) {
-      m_all_options.Append(&m_class_options, LLDB_OPT_SET_1, LLDB_OPT_SET_1);
+      m_all_options.Append(&m_class_options, 
+                           LLDB_OPT_SET_1 | LLDB_OPT_SET_2, 
+                           LLDB_OPT_SET_1);
     }
     m_all_options.Append(&m_options);
     m_all_options.Finalize();
@@ -596,15 +598,15 @@
     }
 
     if (m_step_type == eStepTypeScripted) {
-      if (m_class_options.GetClassName().empty()) {
+      if (m_class_options.GetName().empty()) {
         result.AppendErrorWithFormat("empty class name for scripted step.");
         result.SetStatus(eReturnStatusFailed);
         return false;
       } else if (!GetDebugger().GetScriptInterpreter()->CheckObjectExists(
-                     m_class_options.GetClassName().c_str())) {
+                     m_class_options.GetName().c_str())) {
         result.AppendErrorWithFormat(
             "class for scripted step: \"%s\" does not exist.",
-            m_class_options.GetClassName().c_str());
+            m_class_options.GetName().c_str());
         result.SetStatus(eReturnStatusFailed);
         return false;
       }
@@ -720,7 +722,7 @@
           m_options.m_step_out_avoid_no_debug);
     } else if (m_step_type == eStepTypeScripted) {
       new_plan_sp = thread->QueueThreadPlanForStepScripted(
-          abort_other_plans, m_class_options.GetClassName().c_str(), 
+          abort_other_plans, m_class_options.GetName().c_str(), 
           m_class_options.GetStructuredData(),
           bool_stop_other_threads, new_plan_status);
     } else {
Index: lldb/source/Commands/CommandObjectBreakpointCommand.cpp
===================================================================
--- lldb/source/Commands/CommandObjectBreakpointCommand.cpp
+++ lldb/source/Commands/CommandObjectBreakpointCommand.cpp
@@ -17,6 +17,7 @@
 #include "lldb/Interpreter/CommandInterpreter.h"
 #include "lldb/Interpreter/CommandReturnObject.h"
 #include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionGroupPythonClassWithDict.h"
 #include "lldb/Target/Target.h"
 #include "lldb/Target/Thread.h"
 #include "lldb/Utility/State.h"
@@ -66,7 +67,7 @@
                             nullptr),
         IOHandlerDelegateMultiline("DONE",
                                    IOHandlerDelegate::Completion::LLDBCommand),
-        m_options() {
+        m_options(), m_func_options("breakpoint command", false, 'F') {
     SetHelpLong(
         R"(
 General information about entering breakpoint commands
@@ -201,6 +202,11 @@
         "Final Note: A warning that no breakpoint command was generated when there \
 are no syntax errors may indicate that a function was declared but never called.");
 
+    m_all_options.Append(&m_options);
+    m_all_options.Append(&m_func_options, LLDB_OPT_SET_2 | LLDB_OPT_SET_3, 
+                         LLDB_OPT_SET_2);
+    m_all_options.Finalize();
+    
     CommandArgumentEntry arg;
     CommandArgumentData bp_id_arg;
 
@@ -218,7 +224,7 @@
 
   ~CommandObjectBreakpointCommandAdd() override = default;
 
-  Options *GetOptions() override { return &m_options; }
+  Options *GetOptions() override { return &m_all_options; }
 
   void IOHandlerActivated(IOHandler &io_handler, bool interactive) override {
     StreamFileSP output_sp(io_handler.GetOutputStreamFileSP());
@@ -269,19 +275,20 @@
     }
   }
 
-  class CommandOptions : public Options {
+  class CommandOptions : public OptionGroup {
   public:
     CommandOptions()
-        : Options(), m_use_commands(false), m_use_script_language(false),
+        : OptionGroup(), m_use_commands(false), m_use_script_language(false),
           m_script_language(eScriptLanguageNone), m_use_one_liner(false),
-          m_one_liner(), m_function_name() {}
+          m_one_liner() {}
 
     ~CommandOptions() override = default;
 
     Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
                           ExecutionContext *execution_context) override {
       Status error;
-      const int short_option = m_getopt_table[option_idx].val;
+      const int short_option 
+          = g_breakpoint_command_add_options[option_idx].short_option;
 
       switch (short_option) {
       case 'o':
@@ -313,12 +320,6 @@
               option_arg.str().c_str());
       } break;
 
-      case 'F':
-        m_use_one_liner = false;
-        m_use_script_language = true;
-        m_function_name.assign(option_arg);
-        break;
-
       case 'D':
         m_use_dummy = true;
         break;
@@ -337,7 +338,6 @@
       m_use_one_liner = false;
       m_stop_on_error = true;
       m_one_liner.clear();
-      m_function_name.clear();
       m_use_dummy = false;
     }
 
@@ -355,7 +355,6 @@
     bool m_use_one_liner;
     std::string m_one_liner;
     bool m_stop_on_error;
-    std::string m_function_name;
     bool m_use_dummy;
   };
 
@@ -372,12 +371,9 @@
       return false;
     }
 
-    if (!m_options.m_use_script_language &&
-        !m_options.m_function_name.empty()) {
-      result.AppendError("need to enable scripting to have a function run as a "
-                         "breakpoint command");
-      result.SetStatus(eReturnStatusFailed);
-      return false;
+    if (!m_func_options.GetName().empty()) {
+      m_options.m_use_one_liner = false;
+      m_options.m_use_script_language = true;
     }
 
     BreakpointIDList valid_bp_ids;
@@ -421,9 +417,12 @@
         if (m_options.m_use_one_liner) {
           script_interp->SetBreakpointCommandCallback(
               m_bp_options_vec, m_options.m_one_liner.c_str());
-        } else if (!m_options.m_function_name.empty()) {
-          script_interp->SetBreakpointCommandCallbackFunction(
-              m_bp_options_vec, m_options.m_function_name.c_str());
+        } else if (!m_func_options.GetName().empty()) {
+          Status error = script_interp->SetBreakpointCommandCallbackFunction(
+              m_bp_options_vec, m_func_options.GetName().c_str(),
+              m_func_options.GetStructuredData());
+            if (!error.Success())
+              result.SetError(error);
         } else {
           script_interp->CollectDataForBreakpointCommandCallback(
               m_bp_options_vec, result);
@@ -443,6 +442,9 @@
 
 private:
   CommandOptions m_options;
+  OptionGroupPythonClassWithDict m_func_options;
+  OptionGroupOptions m_all_options;
+
   std::vector<BreakpointOptions *> m_bp_options_vec; // This stores the
                                                      // breakpoint options that
                                                      // we are currently
Index: lldb/source/Commands/CommandObjectBreakpoint.cpp
===================================================================
--- lldb/source/Commands/CommandObjectBreakpoint.cpp
+++ lldb/source/Commands/CommandObjectBreakpoint.cpp
@@ -242,10 +242,10 @@
             interpreter, "breakpoint set",
             "Sets a breakpoint or set of breakpoints in the executable.",
             "breakpoint set <cmd-options>"),
-        m_bp_opts(), m_python_class_options("scripted breakpoint", 'P'),
+        m_bp_opts(), m_python_class_options("scripted breakpoint", true, 'P'),
         m_options() {
     // We're picking up all the normal options, commands and disable.
-    m_all_options.Append(&m_python_class_options, LLDB_OPT_SET_1,
+    m_all_options.Append(&m_python_class_options, LLDB_OPT_SET_1|LLDB_OPT_SET_2,
                          LLDB_OPT_SET_11);
     m_all_options.Append(&m_bp_opts,
                          LLDB_OPT_SET_1 | LLDB_OPT_SET_3 | LLDB_OPT_SET_4,
@@ -543,7 +543,7 @@
 
     BreakpointSetType break_type = eSetTypeInvalid;
 
-    if (!m_python_class_options.GetClassName().empty())
+    if (!m_python_class_options.GetName().empty())
       break_type = eSetTypeScripted;
     else if (m_options.m_line_num != 0)
       break_type = eSetTypeFileAndLine;
@@ -699,7 +699,7 @@
 
       Status error;
       bp_sp = target.CreateScriptedBreakpoint(
-          m_python_class_options.GetClassName().c_str(), &(m_options.m_modules),
+          m_python_class_options.GetName().c_str(), &(m_options.m_modules),
           &(m_options.m_filenames), false, m_options.m_hardware,
           m_python_class_options.GetStructuredData(), &error);
       if (error.Fail()) {
Index: lldb/source/API/SBBreakpointName.cpp
===================================================================
--- lldb/source/API/SBBreakpointName.cpp
+++ lldb/source/API/SBBreakpointName.cpp
@@ -12,11 +12,13 @@
 #include "lldb/API/SBError.h"
 #include "lldb/API/SBStream.h"
 #include "lldb/API/SBStringList.h"
+#include "lldb/API/SBStructuredData.h"
 #include "lldb/API/SBTarget.h"
 
 #include "lldb/Breakpoint/BreakpointName.h"
 #include "lldb/Breakpoint/StoppointCallbackContext.h"
 #include "lldb/Core/Debugger.h"
+#include "lldb/Core/StructuredDataImpl.h"
 #include "lldb/Interpreter/CommandInterpreter.h"
 #include "lldb/Interpreter/ScriptInterpreter.h"
 #include "lldb/Target/Target.h"
@@ -565,24 +567,41 @@
 }
 
 void SBBreakpointName::SetScriptCallbackFunction(
-    const char *callback_function_name) {
-  LLDB_RECORD_METHOD(void, SBBreakpointName, SetScriptCallbackFunction,
-                     (const char *), callback_function_name);
-
+  const char *callback_function_name) {
+LLDB_RECORD_METHOD(void, SBBreakpointName, SetScriptCallbackFunction,
+                   (const char *), callback_function_name);
+  SBStructuredData empty_args;
+  SetScriptCallbackFunction(callback_function_name, empty_args);
+}
+
+SBError SBBreakpointName::SetScriptCallbackFunction(
+    const char *callback_function_name, 
+    SBStructuredData &extra_args) {
+  LLDB_RECORD_METHOD(SBError, SBBreakpointName, SetScriptCallbackFunction,
+                     (const char *, SBStructuredData &), 
+                     callback_function_name, extra_args);
+  SBError sb_error;
   BreakpointName *bp_name = GetBreakpointName();
-  if (!bp_name)
-    return;
+  if (!bp_name) {
+    sb_error.SetErrorString("unrecognized breakpoint name");
+    return LLDB_RECORD_RESULT(sb_error);
+  }
 
   std::lock_guard<std::recursive_mutex> guard(
         m_impl_up->GetTarget()->GetAPIMutex());
 
   BreakpointOptions &bp_options = bp_name->GetOptions();
-  m_impl_up->GetTarget()
+  Status error;
+  error = m_impl_up->GetTarget()
       ->GetDebugger()
       .GetScriptInterpreter()
       ->SetBreakpointCommandCallbackFunction(&bp_options,
-                                             callback_function_name);
+                                             callback_function_name,
+                                             extra_args.m_impl_up
+                                                 ->GetObjectSP());
+  sb_error.SetError(error);
   UpdateName(*bp_name);
+  return LLDB_RECORD_RESULT(sb_error);
 }
 
 SBError
@@ -728,6 +747,8 @@
                        (lldb::SBStream &));
   LLDB_REGISTER_METHOD(void, SBBreakpointName, SetScriptCallbackFunction,
                        (const char *));
+  LLDB_REGISTER_METHOD(SBError, SBBreakpointName, SetScriptCallbackFunction,
+                       (const char *, SBStructuredData &));
   LLDB_REGISTER_METHOD(lldb::SBError, SBBreakpointName, SetScriptCallbackBody,
                        (const char *));
   LLDB_REGISTER_METHOD_CONST(bool, SBBreakpointName, GetAllowList, ());
Index: lldb/source/API/SBBreakpointLocation.cpp
===================================================================
--- lldb/source/API/SBBreakpointLocation.cpp
+++ lldb/source/API/SBBreakpointLocation.cpp
@@ -12,12 +12,14 @@
 #include "lldb/API/SBDebugger.h"
 #include "lldb/API/SBDefines.h"
 #include "lldb/API/SBStream.h"
+#include "lldb/API/SBStructuredData.h"
 #include "lldb/API/SBStringList.h"
 
 #include "lldb/Breakpoint/Breakpoint.h"
 #include "lldb/Breakpoint/BreakpointLocation.h"
 #include "lldb/Core/Debugger.h"
 #include "lldb/Core/StreamFile.h"
+#include "lldb/Core/StructuredDataImpl.h"
 #include "lldb/Interpreter/CommandInterpreter.h"
 #include "lldb/Interpreter/ScriptInterpreter.h"
 #include "lldb/Target/Target.h"
@@ -207,23 +209,38 @@
 }
 
 void SBBreakpointLocation::SetScriptCallbackFunction(
-    const char *callback_function_name) {
-  LLDB_RECORD_METHOD(void, SBBreakpointLocation, SetScriptCallbackFunction,
-                     (const char *), callback_function_name);
+  const char *callback_function_name) {
+LLDB_RECORD_METHOD(void, SBBreakpointLocation, SetScriptCallbackFunction,
+                   (const char *), callback_function_name);
+}
 
+SBError SBBreakpointLocation::SetScriptCallbackFunction(
+    const char *callback_function_name,
+    SBStructuredData &extra_args) {
+  LLDB_RECORD_METHOD(SBError, SBBreakpointLocation, SetScriptCallbackFunction,
+                     (const char *, SBStructuredData &), 
+                     callback_function_name, extra_args);
+  SBError sb_error;
   BreakpointLocationSP loc_sp = GetSP();
 
   if (loc_sp) {
+    Status error;
     std::lock_guard<std::recursive_mutex> guard(
         loc_sp->GetTarget().GetAPIMutex());
     BreakpointOptions *bp_options = loc_sp->GetLocationOptions();
-    loc_sp->GetBreakpoint()
+    error = loc_sp->GetBreakpoint()
         .GetTarget()
         .GetDebugger()
         .GetScriptInterpreter()
         ->SetBreakpointCommandCallbackFunction(bp_options,
-                                               callback_function_name);
-  }
+                                               callback_function_name,
+                                               extra_args.m_impl_up
+                                                   ->GetObjectSP());
+      sb_error.SetError(error);
+    } else
+      sb_error.SetErrorString("invalid breakpoint");
+    
+    return LLDB_RECORD_RESULT(sb_error);
 }
 
 SBError
@@ -482,6 +499,8 @@
   LLDB_REGISTER_METHOD(bool, SBBreakpointLocation, GetAutoContinue, ());
   LLDB_REGISTER_METHOD(void, SBBreakpointLocation, SetScriptCallbackFunction,
                        (const char *));
+  LLDB_REGISTER_METHOD(SBError, SBBreakpointLocation, SetScriptCallbackFunction,
+                       (const char *, SBStructuredData &));
   LLDB_REGISTER_METHOD(lldb::SBError, SBBreakpointLocation,
                        SetScriptCallbackBody, (const char *));
   LLDB_REGISTER_METHOD(void, SBBreakpointLocation, SetCommandLineCommands,
Index: lldb/source/API/SBBreakpoint.cpp
===================================================================
--- lldb/source/API/SBBreakpoint.cpp
+++ lldb/source/API/SBBreakpoint.cpp
@@ -14,6 +14,7 @@
 #include "lldb/API/SBProcess.h"
 #include "lldb/API/SBStream.h"
 #include "lldb/API/SBStringList.h"
+#include "lldb/API/SBStructuredData.h"
 #include "lldb/API/SBThread.h"
 
 #include "lldb/Breakpoint/Breakpoint.h"
@@ -25,6 +26,7 @@
 #include "lldb/Core/Address.h"
 #include "lldb/Core/Debugger.h"
 #include "lldb/Core/StreamFile.h"
+#include "lldb/Core/StructuredDataImpl.h"
 #include "lldb/Interpreter/CommandInterpreter.h"
 #include "lldb/Interpreter/ScriptInterpreter.h"
 #include "lldb/Target/Process.h"
@@ -590,22 +592,38 @@
 }
 
 void SBBreakpoint::SetScriptCallbackFunction(
-    const char *callback_function_name) {
-  LLDB_RECORD_METHOD(void, SBBreakpoint, SetScriptCallbackFunction,
-                     (const char *), callback_function_name);
-
+  const char *callback_function_name) {
+LLDB_RECORD_METHOD(void, SBBreakpoint, SetScriptCallbackFunction,
+                   (const char *), callback_function_name);
+  SBStructuredData empty_args;
+  SetScriptCallbackFunction(callback_function_name, empty_args);
+}
+
+SBError SBBreakpoint::SetScriptCallbackFunction(
+    const char *callback_function_name,
+    SBStructuredData &extra_args) {
+  LLDB_RECORD_METHOD(SBError, SBBreakpoint, SetScriptCallbackFunction,
+  (const char *, SBStructuredData &), callback_function_name, extra_args);
+  SBError sb_error;
   BreakpointSP bkpt_sp = GetSP();
 
   if (bkpt_sp) {
+    Status error;
     std::lock_guard<std::recursive_mutex> guard(
         bkpt_sp->GetTarget().GetAPIMutex());
     BreakpointOptions *bp_options = bkpt_sp->GetOptions();
-    bkpt_sp->GetTarget()
+    error = bkpt_sp->GetTarget()
         .GetDebugger()
         .GetScriptInterpreter()
         ->SetBreakpointCommandCallbackFunction(bp_options,
-                                               callback_function_name);
-  }
+                                               callback_function_name,
+                                               extra_args.m_impl_up
+                                                   ->GetObjectSP());
+    sb_error.SetError(error);
+  } else
+    sb_error.SetErrorString("invalid breakpoint");
+  
+  return LLDB_RECORD_RESULT(sb_error);
 }
 
 SBError SBBreakpoint::SetScriptCallbackBody(const char *callback_body_text) {
@@ -992,6 +1010,8 @@
                        (lldb::SBAddress &));
   LLDB_REGISTER_METHOD(void, SBBreakpoint, SetScriptCallbackFunction,
                        (const char *));
+  LLDB_REGISTER_METHOD(lldb::SBError, SBBreakpoint, SetScriptCallbackFunction,
+                       (const char *, SBStructuredData &));
   LLDB_REGISTER_METHOD(lldb::SBError, SBBreakpoint, SetScriptCallbackBody,
                        (const char *));
   LLDB_REGISTER_METHOD(bool, SBBreakpoint, AddName, (const char *));
Index: lldb/scripts/interface/SBBreakpointName.i
===================================================================
--- lldb/scripts/interface/SBBreakpointName.i
+++ lldb/scripts/interface/SBBreakpointName.i
@@ -84,6 +84,10 @@
 
   void SetScriptCallbackFunction(const char *callback_function_name);
 
+  SBError
+  SetScriptCallbackFunction (const char *callback_function_name,
+                             SBStructuredData &extra_args);
+
   void SetCommandLineCommands(SBStringList &commands);
 
   bool GetCommandLineCommands(SBStringList &commands);
Index: lldb/scripts/interface/SBBreakpointLocation.i
===================================================================
--- lldb/scripts/interface/SBBreakpointLocation.i
+++ lldb/scripts/interface/SBBreakpointLocation.i
@@ -73,10 +73,19 @@
     void SetAutoContinue(bool auto_continue);
 
     %feature("docstring", "
-    Set the callback to the given Python function name.") SetScriptCallbackFunction;
+    Set the callback to the given Python function name.
+    The function takes three arguments (frame, bp_loc, dict).") SetScriptCallbackFunction;
     void
     SetScriptCallbackFunction (const char *callback_function_name);
 
+    %feature("docstring", "
+    Set the name of the script function to be called when the breakpoint is hit.
+    To use this variant, the function should take (frame, bp_loc, extra_args, dict) and
+    when the breakpoint is hit the extra_args will be passed to the callback function.") SetScriptCallbackFunction;
+    SBError
+    SetScriptCallbackFunction (const char *callback_function_name,
+                               SBStructuredData &extra_args);
+
     %feature("docstring", "
     Provide the body for the script function to be called when the breakpoint location is hit.
     The body will be wrapped in a function, which be passed two arguments:
Index: lldb/scripts/interface/SBBreakpoint.i
===================================================================
--- lldb/scripts/interface/SBBreakpoint.i
+++ lldb/scripts/interface/SBBreakpoint.i
@@ -179,6 +179,14 @@
     void
     SetScriptCallbackFunction (const char *callback_function_name);
 
+    %feature("docstring", "
+    Set the name of the script function to be called when the breakpoint is hit.
+    To use this variant, the function should take (frame, bp_loc, extra_args, dict) and
+    when the breakpoint is hit the extra_args will be passed to the callback function.") SetScriptCallbackFunction;
+    SBError
+    SetScriptCallbackFunction (const char *callback_function_name,
+                               SBStructuredData &extra_args);
+
     %feature("docstring", "
     Provide the body for the script function to be called when the breakpoint is hit.
     The body will be wrapped in a function, which be passed two arguments:
Index: lldb/scripts/Python/python-wrapper.swig
===================================================================
--- lldb/scripts/Python/python-wrapper.swig
+++ lldb/scripts/Python/python-wrapper.swig
@@ -45,7 +45,8 @@
     const char *python_function_name,
     const char *session_dictionary_name,
     const lldb::StackFrameSP& frame_sp,
-    const lldb::BreakpointLocationSP& bp_loc_sp
+    const lldb::BreakpointLocationSP& bp_loc_sp,
+    lldb_private::StructuredDataImpl *args_impl
 )
 {
     using namespace lldb_private;
@@ -63,7 +64,19 @@
 
     PythonObject frame_arg(PyRefType::Owned, SBTypeToSWIGWrapper(sb_frame));
     PythonObject bp_loc_arg(PyRefType::Owned, SBTypeToSWIGWrapper(sb_bp_loc));
-    PythonObject result = pfunc(frame_arg, bp_loc_arg, dict);
+
+    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;
+    }
 
     if (result.get() == Py_False)
         stop_at_breakpoint = false;
Index: lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/bktptcmd.py
===================================================================
--- lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/bktptcmd.py
+++ lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/bktptcmd.py
@@ -1,5 +1,23 @@
 from __future__ import print_function
 import side_effect
 
+def useless_function(first, second):
+    print("I have the wrong number of arguments.")
+
 def function(frame, bp_loc, dict):
     side_effect.bktptcmd = "function was here"
+
+def another_function(frame, bp_loc, extra_args, dict):
+    se_value = extra_args.GetValueForKey("side_effect")
+    se_string = se_value.GetStringValue(100)
+    side_effect.fancy = se_string
+
+def a_third_function(frame, bp_loc, extra_args, dict):
+    se_value = extra_args.GetValueForKey("side_effect")
+    se_string = se_value.GetStringValue(100)
+    side_effect.fancier = se_string
+
+def empty_extra_args(frame, bp_loc, extra_args, dict):
+    if extra_args.IsValid():
+        side_effect.not_so_fancy = "Extra args should not be valid"
+    side_effect.not_so_fancy = "Not so fancy"
Index: lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestBreakpointCommandsFromPython.py
===================================================================
--- lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestBreakpointCommandsFromPython.py
+++ lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestBreakpointCommandsFromPython.py
@@ -19,10 +19,15 @@
 
     @add_test_categories(['pyapi'])
     def test_step_out_python(self):
-        """Test stepping out using avoid-no-debug with dsyms."""
+        """Test stepping out using a python breakpoint command."""
         self.build()
         self.do_set_python_command_from_python()
 
+    def test_bkpt_cmd_bad_arguments(self):
+        """Test what happens when pass structured data to a command:"""
+        self.build()
+        self.do_bad_args_to_python_command()
+        
     def setUp(self):
         TestBase.setUp(self)
         self.main_source = "main.c"
@@ -43,6 +48,18 @@
             "Set break point at this line.", self.main_source_spec)
         self.assertTrue(func_bkpt, VALID_BREAKPOINT)
 
+        fancy_bkpt = self.target.BreakpointCreateBySourceRegex(
+            "Set break point at this line.", self.main_source_spec)
+        self.assertTrue(fancy_bkpt, VALID_BREAKPOINT)
+
+        fancier_bkpt = self.target.BreakpointCreateBySourceRegex(
+            "Set break point at this line.", self.main_source_spec)
+        self.assertTrue(fancier_bkpt, VALID_BREAKPOINT)
+
+        not_so_fancy_bkpt = self.target.BreakpointCreateBySourceRegex(
+            "Set break point at this line.", self.main_source_spec)
+        self.assertTrue(not_so_fancy_bkpt, VALID_BREAKPOINT)
+
         # Also test that setting a source regex breakpoint with an empty file
         # spec list sets it on all files:
         no_files_bkpt = self.target.BreakpointCreateBySourceRegex(
@@ -75,14 +92,37 @@
             "Failed to set the script callback body: %s." %
             (error.GetCString()))
 
-        self.dbg.HandleCommand(
-            "command script import --allow-reload ./bktptcmd.py")
+        self.expect("command script import --allow-reload ./bktptcmd.py")
+        
         func_bkpt.SetScriptCallbackFunction("bktptcmd.function")
 
+        extra_args = lldb.SBStructuredData()
+        stream = lldb.SBStream()
+        stream.Print('{"side_effect" : "I am fancy"}')
+        extra_args.SetFromJSON(stream)
+        error = fancy_bkpt.SetScriptCallbackFunction("bktptcmd.another_function", extra_args)
+        self.assertTrue(error.Success(), "Failed to add callback %s"%(error.GetCString()))
+        
+        stream.Clear()
+        stream.Print('{"side_effect" : "I am so much fancier"}')
+        extra_args.SetFromJSON(stream)
+        
+        # Fancier's callback is set up from the command line
+        id = fancier_bkpt.GetID()
+        self.expect("breakpoint command add -F bktptcmd.a_third_function -k side_effect -v 'I am fancier' %d"%(id))
+
+        # Not so fancy gets an empty extra_args:
+        empty_args = lldb.SBStructuredData()
+        error = not_so_fancy_bkpt.SetScriptCallbackFunction("bktptcmd.empty_extra_args", empty_args)
+        self.assertTrue(error.Success(), "Failed to add callback %s"%(error.GetCString()))
+        
         # Clear out canary variables
         side_effect.bktptcmd = None
         side_effect.callback = None
-
+        side_effect.fancy    = None
+        side_effect.fancier  = None
+        side_effect.not_so_fancy = None
+        
         # Now launch the process, and do not stop at entry point.
         self.process = self.target.LaunchSimple(
             None, None, self.get_process_working_directory())
@@ -97,3 +137,38 @@
 
         self.assertEquals("callback was here", side_effect.callback)
         self.assertEquals("function was here", side_effect.bktptcmd)
+        self.assertEquals("I am fancy", side_effect.fancy)
+        self.assertEquals("I am fancier", side_effect.fancier)
+        self.assertEquals("Not so fancy", side_effect.not_so_fancy)
+
+    def do_bad_args_to_python_command(self):
+        exe = self.getBuildArtifact("a.out")
+        error = lldb.SBError()
+
+        self.target = self.dbg.CreateTarget(exe)
+        self.assertTrue(self.target, VALID_TARGET)
+
+
+        self.expect("command script import --allow-reload ./bktptcmd.py")
+
+        bkpt = self.target.BreakpointCreateBySourceRegex(
+            "Set break point at this line.", self.main_source_spec)
+        self.assertTrue(bkpt, VALID_BREAKPOINT)
+
+        # Pass a breakpoint command function that doesn't take extra_args,
+        # but pass it extra args:
+        
+        extra_args = lldb.SBStructuredData()
+        stream = lldb.SBStream()
+        stream.Print('{"side_effect" : "I am fancy"}')
+        extra_args.SetFromJSON(stream)
+
+        error = bkpt.SetScriptCallbackFunction("bktptcmd.function", extra_args)
+        self.assertTrue(error.Fail(), "Can't pass extra args if the function doesn't take them")
+
+        error = bkpt.SetScriptCallbackFunction("bktptcmd.useless_function", extra_args)
+        self.assertTrue(error.Fail(), "Can't pass extra args if the function has wrong number of args.")
+
+        error = bkpt.SetScriptCallbackFunction("bktptcmd.nosuch_function", extra_args)
+        self.assertTrue(error.Fail(), "Can't pass extra args if the function doesn't exist.")
+        
Index: lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestBreakpointCommand.py
===================================================================
--- lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestBreakpointCommand.py
+++ lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestBreakpointCommand.py
@@ -18,7 +18,7 @@
     mydir = TestBase.compute_mydir(__file__)
 
     @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24528")
-    def test_breakpoint_command_sequence(self):
+    def not_test_breakpoint_command_sequence(self):
         """Test a sequence of breakpoint command add, list, and delete."""
         self.build()
         self.breakpoint_command_sequence()
@@ -107,6 +107,10 @@
             "breakpoint command add -s command -o 'frame variable --show-types --scope' 1 4")
         self.runCmd(
             "breakpoint command add -s python -o 'import side_effect; side_effect.one_liner = \"one liner was here\"' 2")
+
+        import side_effect
+        self.runCmd("command script import --allow-reload ./bktptcmd.py")
+
         self.runCmd(
             "breakpoint command add --python-function bktptcmd.function 3")
 
@@ -151,8 +155,6 @@
 
         self.runCmd("breakpoint delete 4")
 
-        self.runCmd("command script import --allow-reload ./bktptcmd.py")
-
         # Next lets try some other breakpoint kinds.  First break with a regular expression
         # and then specify only one file.  The first time we should get two locations,
         # the second time only one:
Index: lldb/include/lldb/Interpreter/ScriptInterpreter.h
===================================================================
--- lldb/include/lldb/Interpreter/ScriptInterpreter.h
+++ lldb/include/lldb/Interpreter/ScriptInterpreter.h
@@ -117,8 +117,10 @@
     return error;
   }
 
-  virtual Status GenerateBreakpointCommandCallbackData(StringList &input,
-                                                       std::string &output) {
+  virtual Status GenerateBreakpointCommandCallbackData(
+      StringList &input,
+      std::string &output,
+      bool has_extra_args) {
     Status error;
     error.SetErrorString("not implemented");
     return error;
@@ -308,14 +310,17 @@
     return error;
   }
 
-  void SetBreakpointCommandCallbackFunction(
+  Status SetBreakpointCommandCallbackFunction(
       std::vector<BreakpointOptions *> &bp_options_vec,
-      const char *function_name);
+      const char *function_name, 
+      StructuredData::ObjectSP extra_args_sp);
 
-  /// Set a one-liner as the callback for the breakpoint.
-  virtual void
-  SetBreakpointCommandCallbackFunction(BreakpointOptions *bp_options,
-                                       const char *function_name) {}
+  /// Set a script function as the callback for the breakpoint.
+  virtual Status
+  SetBreakpointCommandCallbackFunction(
+      BreakpointOptions *bp_options,
+      const char *function_name,
+      StructuredData::ObjectSP extra_args_sp) {}
 
   /// Set a one-liner as the callback for the watchpoint.
   virtual void SetWatchpointCommandCallback(WatchpointOptions *wp_options,
@@ -460,6 +465,12 @@
   const char *GetScriptInterpreterPtyName();
 
   int GetMasterFileDescriptor();
+  
+  virtual llvm::Expected<size_t> 
+  GetNumFixedArgumentsForCallable(const llvm::StringRef &callable_name) { 
+    return llvm::createStringError(
+    llvm::inconvertibleErrorCode(), "Unimplemented function");
+  }
 
   static std::string LanguageToString(lldb::ScriptLanguage language);
 
Index: lldb/include/lldb/Interpreter/OptionGroupPythonClassWithDict.h
===================================================================
--- lldb/include/lldb/Interpreter/OptionGroupPythonClassWithDict.h
+++ lldb/include/lldb/Interpreter/OptionGroupPythonClassWithDict.h
@@ -24,13 +24,10 @@
 class OptionGroupPythonClassWithDict : public OptionGroup {
 public:
   OptionGroupPythonClassWithDict(const char *class_use,
-                      int class_option = 'C',
-                      int key_option = 'k', 
-                      int value_option = 'v',
-                      const char *class_long_option = "python-class",
-                      const char *key_long_option = "python-class-key",
-                      const char *value_long_option = "python-class-value",
-                      bool required = false);
+                                 bool is_class = true,
+                                 int class_option = 'C',
+                                 int key_option = 'k', 
+                                 int value_option = 'v');
                       
   ~OptionGroupPythonClassWithDict() override;
 
@@ -48,16 +45,17 @@
   const StructuredData::DictionarySP GetStructuredData() {
     return m_dict_sp;
   }
-  const std::string &GetClassName() {
-    return m_class_name;
+  const std::string &GetName() {
+    return m_name;
   }
 
 protected:
-  std::string m_class_name;
+  std::string m_name;
   std::string m_current_key;
   StructuredData::DictionarySP m_dict_sp;
   std::string m_class_usage_text, m_key_usage_text, m_value_usage_text;
-  OptionDefinition m_option_definition[3];
+  bool m_is_class;
+  OptionDefinition m_option_definition[4];
 };
 
 } // namespace lldb_private
Index: lldb/include/lldb/API/SBStructuredData.h
===================================================================
--- lldb/include/lldb/API/SBStructuredData.h
+++ lldb/include/lldb/API/SBStructuredData.h
@@ -93,6 +93,9 @@
   friend class SBTarget;
   friend class SBThread;
   friend class SBThreadPlan;
+  friend class SBBreakpoint;
+  friend class SBBreakpointLocation;
+  friend class SBBreakpointName;
 
   StructuredDataImplUP m_impl_up;
 };
Index: lldb/include/lldb/API/SBBreakpointName.h
===================================================================
--- lldb/include/lldb/API/SBBreakpointName.h
+++ lldb/include/lldb/API/SBBreakpointName.h
@@ -85,9 +85,12 @@
 
   void SetScriptCallbackFunction(const char *callback_function_name);
 
-  void SetCommandLineCommands(SBStringList &commands);
+  SBError SetScriptCallbackFunction(const char *callback_function_name,
+                                    SBStructuredData &extra_args);
 
-  bool GetCommandLineCommands(SBStringList &commands);
+  void SetCommandLineCommands(lldb::SBStringList &commands);
+
+  bool GetCommandLineCommands(lldb::SBStringList &commands);
 
   SBError SetScriptCallbackBody(const char *script_body_text);
   
Index: lldb/include/lldb/API/SBBreakpointLocation.h
===================================================================
--- lldb/include/lldb/API/SBBreakpointLocation.h
+++ lldb/include/lldb/API/SBBreakpointLocation.h
@@ -55,11 +55,14 @@
 
   void SetScriptCallbackFunction(const char *callback_function_name);
 
+  SBError SetScriptCallbackFunction(const char *callback_function_name,
+                                    lldb::SBStructuredData &extra_args);
+
   SBError SetScriptCallbackBody(const char *script_body_text);
   
-  void SetCommandLineCommands(SBStringList &commands);
+  void SetCommandLineCommands(lldb::SBStringList &commands);
 
-  bool GetCommandLineCommands(SBStringList &commands);
+  bool GetCommandLineCommands(lldb::SBStringList &commands);
  
   void SetThreadID(lldb::tid_t sb_thread_id);
 
Index: lldb/include/lldb/API/SBBreakpoint.h
===================================================================
--- lldb/include/lldb/API/SBBreakpoint.h
+++ lldb/include/lldb/API/SBBreakpoint.h
@@ -94,6 +94,9 @@
 
   void SetScriptCallbackFunction(const char *callback_function_name);
 
+  SBError SetScriptCallbackFunction(const char *callback_function_name,
+                                 SBStructuredData &extra_args);
+
   void SetCommandLineCommands(SBStringList &commands);
 
   bool GetCommandLineCommands(SBStringList &commands);
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to