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

llvm-svn: 135916
This commit is contained in:
Enrico Granata 2011-07-25 16:59:05 +00:00
parent 6d28d7f2a3
commit c53114e30a
13 changed files with 375 additions and 20 deletions

View File

@ -303,7 +303,7 @@ public:
virtual uint32_t
CalculateNumChildren()
{
if (m_wrapper == NULL)
if (m_wrapper == NULL || m_interpreter == NULL)
return 0;
return m_interpreter->CalculateNumChildren(m_wrapper);
}
@ -311,7 +311,7 @@ public:
virtual lldb::ValueObjectSP
GetChildAtIndex (uint32_t idx, bool can_create)
{
if (m_wrapper == NULL)
if (m_wrapper == NULL || m_interpreter == NULL)
return lldb::ValueObjectSP();
PyObject* py_return = (PyObject*)m_interpreter->GetChildAtIndex(m_wrapper, idx);
@ -323,7 +323,7 @@ public:
lldb::SBValue *sb_ptr = m_interpreter->CastPyObjectToSBValue(py_return);
if (py_return == NULL)
if (py_return == NULL || sb_ptr == NULL)
return lldb::ValueObjectSP();
return sb_ptr->m_opaque_sp;
@ -332,7 +332,7 @@ public:
virtual uint32_t
GetIndexOfChildWithName (const ConstString &name)
{
if (m_wrapper == NULL)
if (m_wrapper == NULL || m_interpreter == NULL)
return UINT32_MAX;
return m_interpreter->GetIndexOfChildWithName(m_wrapper, name.GetCString());
}

View File

@ -15,6 +15,7 @@
#include "lldb/lldb-public.h"
#include "lldb/lldb-enumerations.h"
#include "lldb/Core/Error.h"
#include "lldb/Core/StringList.h"
#include "lldb/Host/Predicate.h"
@ -63,11 +64,13 @@ public:
char* m_end_token;
char* m_prompt;
bool m_echo;
bool m_save_user_input;
public:
InitializationParameters() :
m_baton(NULL),
m_token_size(lldb::eInputReaderGranularityLine),
m_echo(true)
m_echo(true),
m_save_user_input(false)
{
SetEndToken("DONE");
SetPrompt("> ");
@ -80,6 +83,13 @@ public:
return *this;
}
InitializationParameters&
SetSaveUserInput(bool s)
{
m_save_user_input = s;
return *this;
}
InitializationParameters&
SetBaton(void* b)
{
@ -191,6 +201,18 @@ public:
{
return m_echo;
}
StringList&
GetUserInput()
{
return m_user_input;
}
virtual bool
GetSaveUserInput()
{
return false;
}
// Subclasses _can_ override this function to get input as it comes in
// without any granularity
@ -239,6 +261,8 @@ protected:
bool m_echo;
bool m_active;
Predicate<bool> m_reader_done;
StringList m_user_input;
bool m_save_user_input;
private:
DISALLOW_COPY_AND_ASSIGN (InputReader);

View File

@ -75,6 +75,12 @@ public:
virtual void
DoneHandler(HandlerData&) {}
virtual bool
GetSaveUserInput()
{
return m_save_user_input;
}
protected:
friend class Debugger;

View File

@ -101,6 +101,12 @@ public:
return false;
}
virtual bool
GenerateTypeSynthClass (StringList &input, StringList &output)
{
return false;
}
virtual void*
CreateSyntheticScriptedProvider (std::string class_name,
lldb::ValueObjectSP valobj)

View File

@ -52,6 +52,9 @@ public:
bool
GenerateTypeScriptFunction (StringList &input, StringList &output);
bool
GenerateTypeSynthClass (StringList &input, StringList &output);
// use this if the function code is just a one-liner script
bool
GenerateTypeScriptFunction (const char* oneliner, StringList &output);

View File

@ -691,7 +691,6 @@ CommandObjectTypeSummaryAdd::CollectPythonScript (ScriptAddOptions *options,
result.AppendError("out of memory");
result.SetStatus (eReturnStatusFailed);
}
}
bool
@ -2372,6 +2371,148 @@ CommandObjectTypeSynthClear::CommandOptions::g_option_table[] =
// 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 @@ private:
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 @@ private:
m_category = NULL;
m_expr_paths.clear();
is_class_based = false;
handwrite_python = false;
has_child_list = false;
}
const OptionDefinition*
@ -2462,6 +2609,10 @@ private:
bool is_class_based;
bool handwrite_python;
bool has_child_list;
typedef option_vector::iterator ExpressionPathsIterator;
};
@ -2473,6 +2624,61 @@ private:
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 @@ public:
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 @@ CommandObjectTypeSynthAdd::CommandOptions::g_option_table[] =
{ 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 }
};

View File

@ -70,6 +70,37 @@ public:
};
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:

View File

@ -164,15 +164,26 @@ SyntheticScriptProvider::FrontEnd::FrontEnd(std::string pclass,
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)" : "",

View File

@ -25,7 +25,9 @@ InputReader::InputReader (Debugger &debugger) :
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)
{
}

View File

@ -45,7 +45,11 @@ InputReaderEZ::Callback_Impl(void *baton,
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 @@ InputReaderEZ::Initialize(void* baton,
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 ()

View File

@ -131,7 +131,7 @@ ValueObjectSynthetic::GetChildAtIndex (uint32_t idx, bool can_create)
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 @@ ValueObjectSynthetic::GetIndexOfChildWithName (const ConstString &name)
{
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

View File

@ -1249,6 +1249,54 @@ ScriptInterpreterPython::GenerateTypeScriptFunction (StringList &user_input, Str
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)

View File

@ -13,4 +13,4 @@ class fooSynthProvider:
if name == 'a':
return 1;
else:
return 0;
return 0;