Data formatters now cache themselves. This commit provides a new formatter cache mechanism. Upon resolving a formatter (summary or synthetic), LLDB remembers the resolution for later faster retrieval. Also moved the data formatters subsystem from the core to its own group and folder for easier management, and done some code reorganization. The ObjC runtime v1 now returns a class name if asked for the dynamic type of an object. This is required for formatters caching to work with the v1 runtime. Lastly, this commit disposes of the old hack where ValueObjects had to remember whether they were queried for formatters with their static or dynamic type. Now the ValueObjectDynamicValue class works well enough that we can use its dynamic value setting for the same purpose. llvm-svn: 173728
437 lines
15 KiB
C++
437 lines
15 KiB
C++
//===-- OptionValueDictionary.cpp -------------------------------*- C++ -*-===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "lldb/lldb-python.h"
|
|
|
|
#include "lldb/Interpreter/OptionValueDictionary.h"
|
|
|
|
// C Includes
|
|
// C++ Includes
|
|
// Other libraries and framework includes
|
|
#include "llvm/ADT/StringRef.h"
|
|
// Project includes
|
|
#include "lldb/Core/State.h"
|
|
#include "lldb/DataFormatters/FormatManager.h"
|
|
#include "lldb/Interpreter/Args.h"
|
|
#include "lldb/Interpreter/OptionValueString.h"
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
|
|
void
|
|
OptionValueDictionary::DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask)
|
|
{
|
|
const Type dict_type = ConvertTypeMaskToType (m_type_mask);
|
|
if (dump_mask & eDumpOptionType)
|
|
{
|
|
if (m_type_mask != eTypeInvalid)
|
|
strm.Printf ("(%s of %ss)", GetTypeAsCString(), GetBuiltinTypeAsCString(dict_type));
|
|
else
|
|
strm.Printf ("(%s)", GetTypeAsCString());
|
|
}
|
|
if (dump_mask & eDumpOptionValue)
|
|
{
|
|
if (dump_mask & eDumpOptionType)
|
|
strm.PutCString (" =");
|
|
|
|
collection::iterator pos, end = m_values.end();
|
|
|
|
strm.IndentMore();
|
|
|
|
for (pos = m_values.begin(); pos != end; ++pos)
|
|
{
|
|
OptionValue *option_value = pos->second.get();
|
|
strm.EOL();
|
|
strm.Indent(pos->first.GetCString());
|
|
|
|
const uint32_t extra_dump_options = m_raw_value_dump ? eDumpOptionRaw : 0;
|
|
switch (dict_type)
|
|
{
|
|
default:
|
|
case eTypeArray:
|
|
case eTypeDictionary:
|
|
case eTypeProperties:
|
|
case eTypeFileSpecList:
|
|
case eTypePathMap:
|
|
strm.PutChar (' ');
|
|
option_value->DumpValue(exe_ctx, strm, dump_mask | extra_dump_options);
|
|
break;
|
|
|
|
case eTypeBoolean:
|
|
case eTypeEnum:
|
|
case eTypeFileSpec:
|
|
case eTypeFormat:
|
|
case eTypeSInt64:
|
|
case eTypeString:
|
|
case eTypeUInt64:
|
|
case eTypeUUID:
|
|
// No need to show the type for dictionaries of simple items
|
|
strm.PutCString("=");
|
|
option_value->DumpValue(exe_ctx, strm, (dump_mask & (~eDumpOptionType)) | extra_dump_options);
|
|
break;
|
|
}
|
|
}
|
|
strm.IndentLess();
|
|
}
|
|
|
|
}
|
|
|
|
size_t
|
|
OptionValueDictionary::GetArgs (Args &args) const
|
|
{
|
|
args.Clear();
|
|
collection::const_iterator pos, end = m_values.end();
|
|
for (pos = m_values.begin(); pos != end; ++pos)
|
|
{
|
|
StreamString strm;
|
|
strm.Printf("%s=", pos->first.GetCString());
|
|
pos->second->DumpValue(NULL, strm, eDumpOptionValue|eDumpOptionRaw);
|
|
args.AppendArgument(strm.GetString().c_str());
|
|
}
|
|
return args.GetArgumentCount();
|
|
}
|
|
|
|
Error
|
|
OptionValueDictionary::SetArgs (const Args &args, VarSetOperationType op)
|
|
{
|
|
Error error;
|
|
const size_t argc = args.GetArgumentCount();
|
|
switch (op)
|
|
{
|
|
case eVarSetOperationClear:
|
|
Clear();
|
|
break;
|
|
|
|
case eVarSetOperationAppend:
|
|
case eVarSetOperationReplace:
|
|
case eVarSetOperationAssign:
|
|
if (argc > 0)
|
|
{
|
|
for (size_t i=0; i<argc; ++i)
|
|
{
|
|
llvm::StringRef key_and_value(args.GetArgumentAtIndex(i));
|
|
if (!key_and_value.empty())
|
|
{
|
|
std::pair<llvm::StringRef, llvm::StringRef> kvp(key_and_value.split('='));
|
|
llvm::StringRef key = kvp.first;
|
|
bool key_valid = false;
|
|
if (!key.empty())
|
|
{
|
|
if (key.front() == '[')
|
|
{
|
|
// Key name starts with '[', so the the key value must be in single or double quotes like:
|
|
// ['<key>']
|
|
// ["<key>"]
|
|
if ((key.size() > 2) && (key.back() == ']'))
|
|
{
|
|
// Strip leading '[' and trailing ']'
|
|
key = key.substr(1, key.size()-2);
|
|
const char quote_char = key.front();
|
|
if ((quote_char == '\'') || (quote_char == '"'))
|
|
{
|
|
if ((key.size() > 2) && (key.back() == quote_char))
|
|
{
|
|
// Strip the quotes
|
|
key = key.substr(1, key.size()-2);
|
|
key_valid = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// square brackets, no quotes
|
|
key_valid = true;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No square brackets or quotes
|
|
key_valid = true;
|
|
}
|
|
}
|
|
if (!key_valid)
|
|
{
|
|
error.SetErrorStringWithFormat("invalid key \"%s\", the key must be a bare string or surrounded by brackets with optional quotes: [<key>] or ['<key>'] or [\"<key>\"]", kvp.first.str().c_str());
|
|
return error;
|
|
}
|
|
|
|
lldb::OptionValueSP value_sp (CreateValueFromCStringForTypeMask (kvp.second.data(),
|
|
m_type_mask,
|
|
error));
|
|
if (value_sp)
|
|
{
|
|
if (error.Fail())
|
|
return error;
|
|
m_value_was_set = true;
|
|
SetValueForKey (ConstString(key), value_sp, true);
|
|
}
|
|
else
|
|
{
|
|
error.SetErrorString("dictionaries that can contain multiple types must subclass OptionValueArray");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error.SetErrorString("empty argument");
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error.SetErrorString("assign operation takes one or more key=value arguments");
|
|
}
|
|
break;
|
|
|
|
case eVarSetOperationRemove:
|
|
if (argc > 0)
|
|
{
|
|
for (size_t i=0; i<argc; ++i)
|
|
{
|
|
ConstString key(args.GetArgumentAtIndex(i));
|
|
if (!DeleteValueForKey(key))
|
|
{
|
|
error.SetErrorStringWithFormat("no value found named '%s', aborting remove operation", key.GetCString());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error.SetErrorString("remove operation takes one or more key arguments");
|
|
}
|
|
break;
|
|
|
|
case eVarSetOperationInsertBefore:
|
|
case eVarSetOperationInsertAfter:
|
|
case eVarSetOperationInvalid:
|
|
error = OptionValue::SetValueFromCString (NULL, op);
|
|
break;
|
|
}
|
|
return error;
|
|
}
|
|
|
|
Error
|
|
OptionValueDictionary::SetValueFromCString (const char *value_cstr, VarSetOperationType op)
|
|
{
|
|
Args args(value_cstr);
|
|
return SetArgs (args, op);
|
|
}
|
|
|
|
lldb::OptionValueSP
|
|
OptionValueDictionary::GetSubValue (const ExecutionContext *exe_ctx, const char *name, bool will_modify, Error &error) const
|
|
{
|
|
lldb::OptionValueSP value_sp;
|
|
|
|
if (name && name[0])
|
|
{
|
|
const char *sub_name = NULL;
|
|
ConstString key;
|
|
const char *open_bracket = ::strchr (name, '[');
|
|
|
|
if (open_bracket)
|
|
{
|
|
const char *key_start = open_bracket + 1;
|
|
const char *key_end = NULL;
|
|
switch (open_bracket[1])
|
|
{
|
|
case '\'':
|
|
++key_start;
|
|
key_end = strchr(key_start, '\'');
|
|
if (key_end)
|
|
{
|
|
if (key_end[1] == ']')
|
|
{
|
|
if (key_end[2])
|
|
sub_name = key_end + 2;
|
|
}
|
|
else
|
|
{
|
|
error.SetErrorStringWithFormat ("invalid value path '%s', single quoted key names must be formatted as ['<key>'] where <key> is a string that doesn't contain quotes", name);
|
|
return value_sp;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error.SetErrorString ("missing '] key name terminator, key name started with ['");
|
|
return value_sp;
|
|
}
|
|
break;
|
|
case '"':
|
|
++key_start;
|
|
key_end = strchr(key_start, '"');
|
|
if (key_end)
|
|
{
|
|
if (key_end[1] == ']')
|
|
{
|
|
if (key_end[2])
|
|
sub_name = key_end + 2;
|
|
break;
|
|
}
|
|
error.SetErrorStringWithFormat ("invalid value path '%s', double quoted key names must be formatted as [\"<key>\"] where <key> is a string that doesn't contain quotes", name);
|
|
return value_sp;
|
|
}
|
|
else
|
|
{
|
|
error.SetErrorString ("missing \"] key name terminator, key name started with [\"");
|
|
return value_sp;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
key_end = strchr(key_start, ']');
|
|
if (key_end)
|
|
{
|
|
if (key_end[1])
|
|
sub_name = key_end + 1;
|
|
}
|
|
else
|
|
{
|
|
error.SetErrorString ("missing ] key name terminator, key name started with [");
|
|
return value_sp;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (key_start && key_end)
|
|
{
|
|
key.SetCStringWithLength (key_start, key_end - key_start);
|
|
|
|
value_sp = GetValueForKey (key);
|
|
if (value_sp)
|
|
{
|
|
if (sub_name)
|
|
return value_sp->GetSubValue (exe_ctx, sub_name, will_modify, error);
|
|
}
|
|
else
|
|
{
|
|
error.SetErrorStringWithFormat("dictionary does not contain a value for the key name '%s'", key.GetCString());
|
|
}
|
|
}
|
|
}
|
|
if (!value_sp && error.AsCString() == NULL)
|
|
{
|
|
error.SetErrorStringWithFormat ("invalid value path '%s', %s values only support '[<key>]' subvalues where <key> a string value optionally delimitted by single or double quotes",
|
|
name,
|
|
GetTypeAsCString());
|
|
}
|
|
}
|
|
return value_sp;
|
|
}
|
|
|
|
Error
|
|
OptionValueDictionary::SetSubValue (const ExecutionContext *exe_ctx, VarSetOperationType op, const char *name, const char *value)
|
|
{
|
|
Error error;
|
|
const bool will_modify = true;
|
|
lldb::OptionValueSP value_sp (GetSubValue (exe_ctx, name, will_modify, error));
|
|
if (value_sp)
|
|
error = value_sp->SetValueFromCString(value, op);
|
|
else
|
|
{
|
|
if (error.AsCString() == NULL)
|
|
error.SetErrorStringWithFormat("invalid value path '%s'", name);
|
|
}
|
|
return error;
|
|
}
|
|
|
|
|
|
lldb::OptionValueSP
|
|
OptionValueDictionary::GetValueForKey (const ConstString &key) const
|
|
{
|
|
lldb::OptionValueSP value_sp;
|
|
collection::const_iterator pos = m_values.find (key);
|
|
if (pos != m_values.end())
|
|
value_sp = pos->second;
|
|
return value_sp;
|
|
}
|
|
|
|
const char *
|
|
OptionValueDictionary::GetStringValueForKey (const ConstString &key)
|
|
{
|
|
collection::const_iterator pos = m_values.find (key);
|
|
if (pos != m_values.end())
|
|
{
|
|
OptionValueString *string_value = pos->second->GetAsString();
|
|
if (string_value)
|
|
return string_value->GetCurrentValue();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
bool
|
|
OptionValueDictionary::SetStringValueForKey (const ConstString &key,
|
|
const char *value,
|
|
bool can_replace)
|
|
{
|
|
collection::const_iterator pos = m_values.find (key);
|
|
if (pos != m_values.end())
|
|
{
|
|
if (!can_replace)
|
|
return false;
|
|
if (pos->second->GetType() == OptionValue::eTypeString)
|
|
{
|
|
pos->second->SetValueFromCString(value);
|
|
return true;
|
|
}
|
|
}
|
|
m_values[key] = OptionValueSP (new OptionValueString (value));
|
|
return true;
|
|
|
|
}
|
|
|
|
bool
|
|
OptionValueDictionary::SetValueForKey (const ConstString &key,
|
|
const lldb::OptionValueSP &value_sp,
|
|
bool can_replace)
|
|
{
|
|
// Make sure the value_sp object is allowed to contain
|
|
// values of the type passed in...
|
|
if (value_sp && (m_type_mask & value_sp->GetTypeAsMask()))
|
|
{
|
|
if (!can_replace)
|
|
{
|
|
collection::const_iterator pos = m_values.find (key);
|
|
if (pos != m_values.end())
|
|
return false;
|
|
}
|
|
m_values[key] = value_sp;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
OptionValueDictionary::DeleteValueForKey (const ConstString &key)
|
|
{
|
|
collection::iterator pos = m_values.find (key);
|
|
if (pos != m_values.end())
|
|
{
|
|
m_values.erase(pos);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
lldb::OptionValueSP
|
|
OptionValueDictionary::DeepCopy () const
|
|
{
|
|
OptionValueDictionary *copied_dict = new OptionValueDictionary (m_type_mask, m_raw_value_dump);
|
|
lldb::OptionValueSP copied_value_sp(copied_dict);
|
|
collection::const_iterator pos, end = m_values.end();
|
|
for (pos = m_values.begin(); pos != end; ++pos)
|
|
{
|
|
StreamString strm;
|
|
strm.Printf("%s=", pos->first.GetCString());
|
|
copied_dict->SetValueForKey (pos->first, pos->second->DeepCopy(), true);
|
|
}
|
|
return copied_value_sp;
|
|
}
|
|
|