new flag -P to type synth add lets you type a Python class interactively
added a final newline to fooSynthProvider.py
new option to automatically save user input in InputReaderEZ
checking for NULL pointers in several new places
git-svn-id: https://llvm.org/svn/llvm-project/llvdb/trunk@135916 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/source/Commands/CommandObjectType.cpp b/source/Commands/CommandObjectType.cpp
index 0e15dbc..aea2c82 100644
--- a/source/Commands/CommandObjectType.cpp
+++ b/source/Commands/CommandObjectType.cpp
@@ -691,7 +691,6 @@
result.AppendError("out of memory");
result.SetStatus (eReturnStatusFailed);
}
-
}
bool
@@ -2372,6 +2371,148 @@
// CommandObjectTypeSynthAdd
//-------------------------------------------------------------------------
+static const char *g_synth_addreader_instructions = "Enter your Python command(s). Type 'DONE' to end.\n"
+ "You must define a Python class with three methods:\n"
+ "def __init__(self, valobj, dict):\n"
+ "def get_child_at_index(self, index):\n"
+ "def get_child_index(self, name):\n"
+ "class synthProvider:";
+
+class TypeSynthAddInputReader : public InputReaderEZ
+{
+private:
+ DISALLOW_COPY_AND_ASSIGN (TypeSynthAddInputReader);
+public:
+ TypeSynthAddInputReader(Debugger& debugger) :
+ InputReaderEZ(debugger)
+ {}
+
+ virtual
+ ~TypeSynthAddInputReader()
+ {
+ }
+
+ virtual void ActivateHandler(HandlerData& data)
+ {
+ StreamSP out_stream = data.GetOutStream();
+ bool batch_mode = data.GetBatchMode();
+ if (!batch_mode)
+ {
+ out_stream->Printf ("%s\n", g_synth_addreader_instructions);
+ if (data.reader.GetPrompt())
+ out_stream->Printf ("%s", data.reader.GetPrompt());
+ out_stream->Flush();
+ }
+ }
+
+ virtual void ReactivateHandler(HandlerData& data)
+ {
+ StreamSP out_stream = data.GetOutStream();
+ bool batch_mode = data.GetBatchMode();
+ if (data.reader.GetPrompt() && !batch_mode)
+ {
+ out_stream->Printf ("%s", data.reader.GetPrompt());
+ out_stream->Flush();
+ }
+ }
+ virtual void GotTokenHandler(HandlerData& data)
+ {
+ StreamSP out_stream = data.GetOutStream();
+ bool batch_mode = data.GetBatchMode();
+ if (data.bytes && data.bytes_len && data.baton)
+ {
+ ((SynthAddOptions*)data.baton)->m_user_source.AppendString(data.bytes, data.bytes_len);
+ }
+ if (!data.reader.IsDone() && data.reader.GetPrompt() && !batch_mode)
+ {
+ out_stream->Printf ("%s", data.reader.GetPrompt());
+ out_stream->Flush();
+ }
+ }
+ virtual void InterruptHandler(HandlerData& data)
+ {
+ StreamSP out_stream = data.GetOutStream();
+ bool batch_mode = data.GetBatchMode();
+ data.reader.SetIsDone (true);
+ if (!batch_mode)
+ {
+ out_stream->Printf ("Warning: No command attached to breakpoint.\n");
+ out_stream->Flush();
+ }
+ }
+ virtual void EOFHandler(HandlerData& data)
+ {
+ data.reader.SetIsDone (true);
+ }
+ virtual void DoneHandler(HandlerData& data)
+ {
+ StreamSP out_stream = data.GetOutStream();
+ SynthAddOptions *options_ptr = ((SynthAddOptions*)data.baton);
+ if (!options_ptr)
+ {
+ out_stream->Printf ("Internal error #1: no script attached.\n");
+ out_stream->Flush();
+ return;
+ }
+
+ SynthAddOptions::SharedPointer options(options_ptr); // this will ensure that we get rid of the pointer when going out of scope
+
+ ScriptInterpreter *interpreter = data.reader.GetDebugger().GetCommandInterpreter().GetScriptInterpreter();
+ if (!interpreter)
+ {
+ out_stream->Printf ("Internal error #2: no script attached.\n");
+ out_stream->Flush();
+ return;
+ }
+ StringList class_name_sl;
+ if (!interpreter->GenerateTypeSynthClass (options->m_user_source,
+ class_name_sl))
+ {
+ out_stream->Printf ("Internal error #3: no script attached.\n");
+ out_stream->Flush();
+ return;
+ }
+ if (class_name_sl.GetSize() == 0)
+ {
+ out_stream->Printf ("Internal error #4: no script attached.\n");
+ out_stream->Flush();
+ return;
+ }
+ const char *class_name = class_name_sl.GetStringAtIndex(0);
+ if (!class_name || !class_name[0])
+ {
+ out_stream->Printf ("Internal error #5: no script attached.\n");
+ out_stream->Flush();
+ return;
+ }
+
+ // everything should be fine now, let's add the synth provider class
+
+ SyntheticChildrenSP synth_provider;
+ synth_provider.reset(new SyntheticScriptProvider(options->m_cascade,
+ options->m_skip_pointers,
+ options->m_skip_references,
+ std::string(class_name)));
+
+
+ lldb::FormatCategorySP category;
+ Debugger::Formatting::Categories::Get(ConstString(options->m_category), category);
+
+ for (size_t i = 0; i < options->m_target_types.GetSize(); i++) {
+ const char *type_name = options->m_target_types.GetStringAtIndex(i);
+ ConstString typeCS(type_name);
+ if (typeCS)
+ category->Filter()->Add(typeCS.GetCString(), synth_provider);
+ else
+ {
+ out_stream->Printf ("Internal error #6: no script attached.\n");
+ out_stream->Flush();
+ return;
+ }
+ }
+ }
+};
+
class CommandObjectTypeSynthAdd : public CommandObject
{
@@ -2406,6 +2547,10 @@
break;
case 'c':
m_expr_paths.push_back(option_arg);
+ has_child_list = true;
+ break;
+ case 'P':
+ handwrite_python = true;
break;
case 'l':
m_class_name = std::string(option_arg);
@@ -2438,6 +2583,8 @@
m_category = NULL;
m_expr_paths.clear();
is_class_based = false;
+ handwrite_python = false;
+ has_child_list = false;
}
const OptionDefinition*
@@ -2462,6 +2609,10 @@
bool is_class_based;
+ bool handwrite_python;
+
+ bool has_child_list;
+
typedef option_vector::iterator ExpressionPathsIterator;
};
@@ -2473,6 +2624,61 @@
return &m_options;
}
+ void
+ CollectPythonScript (SynthAddOptions *options,
+ CommandReturnObject &result)
+ {
+ InputReaderSP reader_sp (new TypeSynthAddInputReader(m_interpreter.GetDebugger()));
+ if (reader_sp && options)
+ {
+
+ InputReaderEZ::InitializationParameters ipr;
+
+ Error err (reader_sp->Initialize (ipr.SetBaton(options).SetPrompt(" ")));
+ if (err.Success())
+ {
+ m_interpreter.GetDebugger().PushInputReader (reader_sp);
+ result.SetStatus (eReturnStatusSuccessFinishNoResult);
+ }
+ else
+ {
+ result.AppendError (err.AsCString());
+ result.SetStatus (eReturnStatusFailed);
+ }
+ }
+ else
+ {
+ result.AppendError("out of memory");
+ result.SetStatus (eReturnStatusFailed);
+ }
+ }
+
+ bool
+ Execute_HandwritePython (Args& command, CommandReturnObject &result)
+ {
+ SynthAddOptions *options = new SynthAddOptions ( m_options.m_skip_pointers,
+ m_options.m_skip_references,
+ m_options.m_cascade,
+ m_options.m_category);
+
+ const size_t argc = command.GetArgumentCount();
+
+ for (size_t i = 0; i < argc; i++) {
+ const char* typeA = command.GetArgumentAtIndex(i);
+ if (typeA && *typeA)
+ options->m_target_types << typeA;
+ else
+ {
+ result.AppendError("empty typenames not allowed");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+
+ CollectPythonScript(options,result);
+ return result.Succeeded();
+ }
+
bool
Execute_ChildrenList (Args& command, CommandReturnObject &result)
{
@@ -2606,10 +2812,18 @@
bool
Execute (Args& command, CommandReturnObject &result)
{
- if (m_options.is_class_based)
+ if (m_options.handwrite_python)
+ return Execute_HandwritePython(command, result);
+ else if (m_options.is_class_based)
return Execute_PythonClass(command, result);
- else
+ else if (m_options.has_child_list)
return Execute_ChildrenList(command, result);
+ else
+ {
+ result.AppendError("must either provide a children list, a Python class name, or use -P and type a Python class line-by-line");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
}
};
@@ -2622,6 +2836,7 @@
{ LLDB_OPT_SET_ALL, false, "category", 'w', required_argument, NULL, 0, eArgTypeName, "Add this to the given category instead of the default one."},
{ LLDB_OPT_SET_1, false, "child", 'c', required_argument, NULL, 0, eArgTypeName, "Include this expression path in the synthetic view."},
{ LLDB_OPT_SET_2, false, "python-class", 'l', required_argument, NULL, 0, eArgTypeName, "Use this Python class to produce synthetic children."},
+ { LLDB_OPT_SET_3, false, "input-python", 'P', no_argument, NULL, 0, eArgTypeBoolean, "Type Python code to generate a class that provides synthetic children."},
{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL }
};
diff --git a/source/Commands/CommandObjectType.h b/source/Commands/CommandObjectType.h
index c37bc70..1e5279d 100644
--- a/source/Commands/CommandObjectType.h
+++ b/source/Commands/CommandObjectType.h
@@ -70,6 +70,37 @@
};
+class SynthAddOptions
+{
+
+public:
+
+ bool m_skip_pointers;
+ bool m_skip_references;
+ bool m_cascade;
+ StringList m_user_source;
+ StringList m_target_types;
+
+ const char* m_category;
+
+ SynthAddOptions(bool sptr,
+ bool sref,
+ bool casc,
+ const char* catg) :
+ m_skip_pointers(sptr),
+ m_skip_references(sref),
+ m_cascade(casc),
+ m_user_source(),
+ m_target_types(),
+ m_category(catg)
+ {
+ }
+
+ typedef lldb::SharedPtr<SynthAddOptions>::Type SharedPointer;
+
+};
+
+
class CommandObjectType : public CommandObjectMultiword
{
public:
diff --git a/source/Core/FormatClasses.cpp b/source/Core/FormatClasses.cpp
index 9a232ae..e321a40 100644
--- a/source/Core/FormatClasses.cpp
+++ b/source/Core/FormatClasses.cpp
@@ -164,15 +164,26 @@
SyntheticChildrenFrontEnd(be),
m_python_class(pclass)
{
+ if (be.get() == NULL)
+ {
+ m_interpreter = NULL;
+ m_wrapper = NULL;
+ return;
+ }
+
m_interpreter = be->GetUpdatePoint().GetTarget()->GetDebugger().GetCommandInterpreter().GetScriptInterpreter();
- m_wrapper = (PyObject*)m_interpreter->CreateSyntheticScriptedProvider(m_python_class, m_backend);
+
+ if (m_interpreter == NULL)
+ m_wrapper = NULL;
+ else
+ m_wrapper = (PyObject*)m_interpreter->CreateSyntheticScriptedProvider(m_python_class, m_backend);
}
std::string
SyntheticScriptProvider::GetDescription()
{
StreamString sstr;
- sstr.Printf("%s%s%s Python class: %s",
+ sstr.Printf("%s%s%s Python class %s",
m_cascades ? "" : " (not cascading)",
m_skip_pointers ? " (skip pointers)" : "",
m_skip_references ? " (skip references)" : "",
diff --git a/source/Core/InputReader.cpp b/source/Core/InputReader.cpp
index 84cd0a1..ebde2be 100644
--- a/source/Core/InputReader.cpp
+++ b/source/Core/InputReader.cpp
@@ -25,7 +25,9 @@
m_done (true),
m_echo (true),
m_active (false),
- m_reader_done (false)
+ m_reader_done (false),
+ m_user_input(),
+ m_save_user_input(false)
{
}
diff --git a/source/Core/InputReaderEZ.cpp b/source/Core/InputReaderEZ.cpp
index 295bfb8..c8362fb 100644
--- a/source/Core/InputReaderEZ.cpp
+++ b/source/Core/InputReaderEZ.cpp
@@ -45,7 +45,11 @@
reader.AsynchronousOutputWrittenHandler(hand_data);
break;
case eInputReaderGotToken:
+ {
+ if (reader.GetSaveUserInput())
+ reader.GetUserInput().AppendString(bytes, bytes_len);
reader.GotTokenHandler(hand_data);
+ }
break;
case eInputReaderInterrupt:
reader.InterruptHandler(hand_data);
@@ -78,11 +82,13 @@
Error
InputReaderEZ::Initialize(InitializationParameters& params)
{
- return Initialize(params.m_baton,
- params.m_token_size,
- params.m_end_token,
- params.m_prompt,
- params.m_echo);
+ Error ret = Initialize(params.m_baton,
+ params.m_token_size,
+ params.m_end_token,
+ params.m_prompt,
+ params.m_echo);
+ m_save_user_input = params.m_save_user_input;
+ return ret;
}
InputReaderEZ::~InputReaderEZ ()
diff --git a/source/Core/ValueObjectSyntheticFilter.cpp b/source/Core/ValueObjectSyntheticFilter.cpp
index d7d00b6..252780c 100644
--- a/source/Core/ValueObjectSyntheticFilter.cpp
+++ b/source/Core/ValueObjectSyntheticFilter.cpp
@@ -131,7 +131,7 @@
if (iter == m_children_byindex.end())
{
- if (can_create)
+ if (can_create && m_synth_filter != NULL)
{
lldb::ValueObjectSP synth_guy = m_synth_filter->GetChildAtIndex (idx, can_create);
m_children_byindex[idx]= synth_guy;
@@ -161,13 +161,16 @@
{
NameToIndexIterator iter = m_name_toindex.find(name.GetCString());
- if (iter == m_name_toindex.end())
+ if (iter == m_name_toindex.end() && m_synth_filter != NULL)
{
uint32_t index = m_synth_filter->GetIndexOfChildWithName (name);
m_name_toindex[name.GetCString()] = index;
return index;
}
- return iter->second;
+ else if (iter == m_name_toindex.end() && m_synth_filter == NULL)
+ return UINT32_MAX;
+ else /*if (iter != m_name_toindex.end())*/
+ return iter->second;
}
bool
diff --git a/source/Interpreter/ScriptInterpreterPython.cpp b/source/Interpreter/ScriptInterpreterPython.cpp
index 03eadc6..82cad85 100644
--- a/source/Interpreter/ScriptInterpreterPython.cpp
+++ b/source/Interpreter/ScriptInterpreterPython.cpp
@@ -1249,6 +1249,54 @@
return true;
}
+bool
+ScriptInterpreterPython::GenerateTypeSynthClass (StringList &user_input, StringList &output)
+{
+ static int num_created_classes = 0;
+ user_input.RemoveBlankLines ();
+ int num_lines = user_input.GetSize ();
+ StreamString sstr;
+
+ // Check to see if we have any data; if not, just return.
+ if (user_input.GetSize() == 0)
+ return false;
+
+ // Wrap all user input into a Python class
+
+ sstr.Printf ("lldb_autogen_python_type_synth_class_%d", num_created_classes);
+ ++num_created_classes;
+ std::string auto_generated_class_name = sstr.GetData();
+
+ sstr.Clear();
+ StringList auto_generated_class;
+
+ // Create the function name & definition string.
+
+ sstr.Printf ("class %s:", auto_generated_class_name.c_str());
+ auto_generated_class.AppendString (sstr.GetData());
+
+ // Wrap everything up inside the class, increasing the indentation.
+
+ for (int i = 0; i < num_lines; ++i)
+ {
+ sstr.Clear ();
+ sstr.Printf (" %s", user_input.GetStringAtIndex (i));
+ auto_generated_class.AppendString (sstr.GetData());
+ }
+
+
+ // Verify that the results are valid Python.
+ // (even though the method is ExportFunctionDefinitionToInterpreter, a class will actually be exported)
+ // (TODO: rename that method to ExportDefinitionToInterpreter)
+ if (!ExportFunctionDefinitionToInterpreter (auto_generated_class))
+ return false;
+
+ // Store the name of the auto-generated class
+
+ output.AppendString (auto_generated_class_name.c_str());
+ return true;
+}
+
void*
ScriptInterpreterPython::CreateSyntheticScriptedProvider (std::string class_name,
lldb::ValueObjectSP valobj)