/************************************************************************** ** ** This file is part of Qt Creator ** ** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** Commercial Usage ** ** Licensees holding valid Qt Commercial licenses may use this file in ** accordance with the Qt Commercial License Agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Nokia. ** ** GNU Lesser General Public License Usage ** ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** If you are unsure which license is appropriate for your use, please ** contact the sales department at http://qt.nokia.com/contact. ** **************************************************************************/ #include "symbolgroup.h" #include "stringutils.h" #include "base64.h" #include #include #include #include #include enum { debug = 0 }; typedef std::vector::size_type VectorIndexType; typedef std::vector StringVector; const char rootNameC[] = "local"; enum { BufSize = 2048 }; std::ostream &operator<<(std::ostream &str, const DEBUG_SYMBOL_PARAMETERS ¶meters) { str << "parent="; if (parameters.ParentSymbol == DEBUG_ANY_ID) { str << "DEBUG_ANY_ID"; } else { str << parameters.ParentSymbol ; } if (parameters.Flags != 0 && parameters.Flags != 1) str << " flags=" << parameters.Flags; // Detailed flags: if (parameters.Flags & DEBUG_SYMBOL_EXPANDED) str << " EXPANDED"; if (parameters.Flags & DEBUG_SYMBOL_READ_ONLY) str << " READONLY"; if (parameters.Flags & DEBUG_SYMBOL_IS_ARRAY) str << " ARRAY"; if (parameters.Flags & DEBUG_SYMBOL_IS_FLOAT) str << " FLOAT"; if (parameters.Flags & DEBUG_SYMBOL_IS_ARGUMENT) str << " ARGUMENT"; if (parameters.Flags & DEBUG_SYMBOL_IS_LOCAL) str << " LOCAL"; str << " typeId=" << parameters.TypeId; if (parameters.SubElements) str << " subElements=" << parameters.SubElements; return str; } SymbolGroup::SymbolGroup(IDebugSymbolGroup2 *sg, const SymbolParameterVector &vec, ULONG threadId, unsigned frame) : m_symbolGroup(sg), m_threadId(threadId), m_frame(frame), m_root(0) { m_root = SymbolGroupNode::create(this, rootNameC, vec); } SymbolGroup::~SymbolGroup() { m_symbolGroup->Release(); delete m_root; } static inline bool getSymbolCount(CIDebugSymbolGroup *symbolGroup, ULONG *count, std::string *errorMessage) { const HRESULT hr = symbolGroup->GetNumberSymbols(count); if (FAILED(hr)) { *errorMessage = msgDebugEngineComFailed("GetNumberSymbols", hr); return false; } return true; } bool SymbolGroup::getSymbolParameters(CIDebugSymbolGroup *symbolGroup, SymbolParameterVector *vec, std::string *errorMessage) { ULONG count; return getSymbolCount(symbolGroup, &count, errorMessage) && getSymbolParameters(symbolGroup, 0, count, vec, errorMessage); } bool SymbolGroup::getSymbolParameters(CIDebugSymbolGroup *symbolGroup, unsigned long start, unsigned long count, SymbolParameterVector *vec, std::string *errorMessage) { if (!count) { vec->clear(); return true; } // Trim the count to the maximum count available. When expanding elements // and passing SubElements as count, SubElements might be an estimate that // is too large and triggers errors. ULONG totalCount; if (!getSymbolCount(symbolGroup, &totalCount, errorMessage)) return false; if (start >= totalCount) { std::ostringstream str; str << "SymbolGroup::getSymbolParameters: Start parameter " << start << " beyond total " << totalCount << '.'; *errorMessage = str.str(); return false; } if (start + count > totalCount) count = totalCount - start; // Get parameters. vec->resize(count); const HRESULT hr = symbolGroup->GetSymbolParameters(start, count, &(*vec->begin())); if (FAILED(hr)) { std::ostringstream str; str << "SymbolGroup::getSymbolParameters failed for index=" << start << ", count=" << count << ": " << msgDebugEngineComFailed("GetSymbolParameters", hr); *errorMessage = str.str(); return false; } return true; } SymbolGroup *SymbolGroup::create(CIDebugControl *control, CIDebugSymbols *debugSymbols, ULONG threadId, unsigned frame, std::string *errorMessage) { errorMessage->clear(); ULONG obtainedFrameCount = 0; const ULONG frameCount = frame + 1; DEBUG_STACK_FRAME *frames = new DEBUG_STACK_FRAME[frameCount]; IDebugSymbolGroup2 *idebugSymbols = 0; bool success = false; SymbolParameterVector parameters; // Obtain symbol group at stack frame. do { HRESULT hr = control->GetStackTrace(0, 0, 0, frames, frameCount, &obtainedFrameCount); if (FAILED(hr)) { *errorMessage = msgDebugEngineComFailed("GetStackTrace", hr); break; } if (obtainedFrameCount < frameCount ) { std::ostringstream str; str << "Unable to obtain frame " << frame << " (" << obtainedFrameCount << ")."; *errorMessage = str.str(); break; } hr = debugSymbols->GetScopeSymbolGroup2(DEBUG_SCOPE_GROUP_LOCALS, NULL, &idebugSymbols); if (FAILED(hr)) { *errorMessage = msgDebugEngineComFailed("GetScopeSymbolGroup2", hr); break; } hr = debugSymbols->SetScope(0, frames + frame, NULL, 0); if (FAILED(hr)) { *errorMessage = msgDebugEngineComFailed("SetScope", hr); break; } // refresh with current frame hr = debugSymbols->GetScopeSymbolGroup2(DEBUG_SCOPE_GROUP_LOCALS, idebugSymbols, &idebugSymbols); if (FAILED(hr)) { *errorMessage = msgDebugEngineComFailed("GetScopeSymbolGroup2", hr); break; } if (!SymbolGroup::getSymbolParameters(idebugSymbols, ¶meters, errorMessage)) break; success = true; } while (false); delete [] frames; if (!success) { if (idebugSymbols) idebugSymbols->Release(); return 0; } return new SymbolGroup(idebugSymbols, parameters, threadId, frame); } // ------- SymbolGroupNode SymbolGroupNode::SymbolGroupNode(SymbolGroup *symbolGroup, ULONG index, const std::string &name, const std::string &iname, SymbolGroupNode *parent) : m_symbolGroup(symbolGroup), m_parent(parent), m_index(index), m_name(name), m_iname(iname), m_flags(0) { memset(&m_parameters, 0, sizeof(DEBUG_SYMBOL_PARAMETERS)); m_parameters.ParentSymbol = DEBUG_ANY_ID; } void SymbolGroupNode::removeChildren() { if (!m_children.empty()) { const SymbolGroupNodePtrVectorIterator end = m_children.end(); for (SymbolGroupNodePtrVectorIterator it = m_children.begin(); it != end; ++it) delete *it; m_children.clear(); } } bool SymbolGroupNode::isArrayElement() const { return m_parent && (m_parent->m_parameters.Flags & DEBUG_SYMBOL_IS_ARRAY); } // Notify about expansion of a node: // Adapt our index and those of our children if we are behind it. // Return true if a modification was required to be able to terminate the // recursion. bool SymbolGroupNode::notifyExpanded(ULONG index, ULONG insertedCount) { typedef SymbolGroupNodePtrVector::reverse_iterator ReverseIt; // Looping backwards over the children. If a subtree has no modifications, // (meaning all other indexes are smaller) we can stop. const ReverseIt rend = m_children.rend(); for (ReverseIt it = m_children.rbegin(); it != rend; ++it) if (!(*it)->notifyExpanded(index, insertedCount)) return false; // Correct our own + parent index if applicable. if (m_index == DEBUG_ANY_ID || m_index < index) return false; m_index += insertedCount; if (m_parameters.ParentSymbol != DEBUG_ANY_ID && m_parameters.ParentSymbol >= index) m_parameters.ParentSymbol += insertedCount; return true; } // Return full iname as 'locals.this.m_sth'. std::string SymbolGroupNode::fullIName() const { std::string rc = iName(); for (const SymbolGroupNode *p = parent(); p; p = p->parent()) { rc.insert(0, 1, '.'); rc.insert(0, p->iName()); } return rc; } // Fix names: fix complicated template base names static inline void fixName(std::string *name) { // Long template base classes 'std::tree_base' -> 'std::tree<>' // for nice display const std::string::size_type templatePos = name->find('<'); if (templatePos != std::string::npos) { name->erase(templatePos + 1, name->size() - templatePos - 1); name->push_back('>'); } } // Fix inames: arrays and long, complicated template base names static inline void fixIname(unsigned &id, std::string *iname) { // Fix array iname "[0]" -> "0" for sorting to work correctly if (!iname->empty() && iname->at(0) == '[') { const std::string::size_type last = iname->size() - 1; if (iname->at(last) == ']') { iname->erase(last, 1); iname->erase(0, 1); return; } } // Long template base classes 'std::tree_base 'tree@t1', // usable as identifier and command line parameter const std::string::size_type templatePos = iname->find('<'); if (templatePos != std::string::npos) { iname->erase(templatePos, iname->size() - templatePos); if (iname->compare(0, 5, "std::") == 0) iname->erase(0, 5); iname->append("@t"); iname->append(toString(id++)); } } // Fix up names and inames static inline void fixNames(bool isTopLevel, StringVector *names, StringVector *inames) { if (names->empty()) return; unsigned unnamedId = 1; unsigned templateId = 1; /* 1) Fix name="__formal", which occurs when someone writes "void foo(int /* x * /)..." * 2) Fix array inames for sorting: "[6]" -> name="[6]",iname="6" * 3) For toplevels: Fix shadowed variables in the order the debugger expects them: \code int x; // Occurrence (1), should be reported as name="x "/iname="x#1" if (true) { int x = 5; (2) // Occurrence (2), should be reported as name="x"/iname="x" } \endcode */ StringVector::iterator nameIt = names->begin(); const StringVector::iterator namesEnd = names->end(); for (StringVector::iterator iNameIt = inames->begin(); nameIt != namesEnd ; ++nameIt, ++iNameIt) { std::string &name = *nameIt; std::string &iname = *iNameIt; if (name.empty() || name == "__formal") { const std::string number = toString(unnamedId++); name = "'; iname = "unnamed#" + number; } else { fixName(&name); fixIname(templateId, &iname); } if (isTopLevel) { if (const StringVector::size_type shadowCount = std::count(nameIt + 1, namesEnd, name)) { const std::string number = toString(shadowCount); name += " 1) m_children.reserve(m_parameters.SubElements); const VectorIndexType size = vec.size(); // Scan the top level elements StringVector names; names.reserve(size); // Pass 1) Determine names. We need the complete set first in order to do some corrections. const VectorIndexType startIndex = isTopLevel ? 0 : index + 1; for (VectorIndexType pos = startIndex - parameterOffset; pos < size ; pos++ ) { if (vec.at(pos).ParentSymbol == index) { const VectorIndexType symbolGroupIndex = pos + parameterOffset; if (FAILED(m_symbolGroup->debugSymbolGroup()->GetSymbolName(ULONG(symbolGroupIndex), buf, BufSize, &obtainedSize))) buf[0] = '\0'; names.push_back(std::string(buf)); } } // 2) Fix names StringVector inames = names; fixNames(isTopLevel, &names, &inames); // Pass 3): Add nodes with fixed names StringVector::size_type nameIndex = 0; for (VectorIndexType pos = startIndex - parameterOffset; pos < size ; pos++ ) { if (vec.at(pos).ParentSymbol == index) { const VectorIndexType symbolGroupIndex = pos + parameterOffset; SymbolGroupNode *child = new SymbolGroupNode(m_symbolGroup, ULONG(symbolGroupIndex), names.at(nameIndex), inames.at(nameIndex), this); child->parseParameters(symbolGroupIndex, parameterOffset, vec); m_children.push_back(child); nameIndex++; } } if (isTopLevel) m_parameters.SubElements = ULONG(m_children.size()); } SymbolGroupNode *SymbolGroupNode::create(SymbolGroup *sg, const std::string &name, const SymbolGroup::SymbolParameterVector &vec) { SymbolGroupNode *rc = new SymbolGroupNode(sg, DEBUG_ANY_ID, name, name); rc->parseParameters(DEBUG_ANY_ID, 0, vec); return rc; } // Fix some oddities in CDB values static void fixValue(const std::string &type, std::wstring *value) { // Pointers: fix '0x00000000`00000AD class bla' ... to "0xAD", but leave // 'const char *' values as is. if (!type.empty() && type.at(type.size() - 1) == L'*' && value->compare(0, 2, L"0x") == 0) { // Remove dumb 64bit separator if (value->size() > 10 && value->at(10) == L'`') value->erase(10, 1); const std::string::size_type firstNonNullDigit = value->find_first_not_of(L"0", 2); // No on-null digits: plain null ptr. if (firstNonNullDigit == std::string::npos || value->at(firstNonNullDigit) == ' ') { *value = L"0x0"; return; } // Strip if (firstNonNullDigit > 2) value->erase(2, firstNonNullDigit - 2); // Strip ' Class bla" std::wstring::size_type classPos = value->find(L" struct", 2); if (classPos == std::string::npos) classPos = value->find(L" class", 2); if (classPos != std::string::npos) value->erase(classPos, value->size() - classPos); return; } // Integers: fix '0n10' -> '10' if (value->size() >= 3 && value->compare(0, 2, L"0n") == 0 && (isdigit(value->at(2)) || value->at(2) == L'-')) { value->erase(0, 2); return; } } // Check for ASCII-encode-able stuff. Plain characters + tabs at the most, no newline. static bool isSevenBitClean(const wchar_t *buf, ULONG size) { const wchar_t *bufEnd = buf + size; for (const wchar_t *bufPtr = buf; bufPtr < bufEnd; bufPtr++) { const wchar_t c = *bufPtr; if (c > 127 || (c < 32 && c != 9)) return false; } return true; } std::string SymbolGroupNode::getType() const { static char buf[BufSize]; const HRESULT hr = m_symbolGroup->debugSymbolGroup()->GetSymbolTypeName(m_index, buf, BufSize, NULL); return SUCCEEDED(hr) ? std::string(buf) : std::string(); } wchar_t *SymbolGroupNode::getValue(ULONG *obtainedSizeIn /* = 0 */) const { // Determine size and return allocated buffer if (obtainedSizeIn) *obtainedSizeIn = 0; const ULONG maxValueSize = 262144; ULONG obtainedSize = 0; HRESULT hr = m_symbolGroup->debugSymbolGroup()->GetSymbolValueTextWide(m_index, NULL, maxValueSize, &obtainedSize); if (FAILED(hr)) return 0; if (obtainedSize > maxValueSize) obtainedSize = maxValueSize; wchar_t *buffer = new wchar_t[obtainedSize]; hr = m_symbolGroup->debugSymbolGroup()->GetSymbolValueTextWide(m_index, buffer, obtainedSize, &obtainedSize); if (FAILED(hr)) { // Whoops, should not happen delete [] buffer; return 0; } if (obtainedSizeIn) *obtainedSizeIn = obtainedSize; return buffer; } ULONG64 SymbolGroupNode::address() const { ULONG64 address = 0; const HRESULT hr = m_symbolGroup->debugSymbolGroup()->GetSymbolOffset(m_index, &address); if (SUCCEEDED(hr)) return address; return 0; } std::wstring SymbolGroupNode::rawValue() const { std::wstring rc; if (const wchar_t *wbuf = getValue()) { rc = wbuf; delete[] wbuf; } return rc; } std::wstring SymbolGroupNode::fixedValue() const { std::wstring value = rawValue(); fixValue(getType(), &value); return value; } SymbolGroupNode *SymbolGroupNode::childAt(unsigned i) const { return i < m_children.size() ? m_children.at(i) : static_cast(0); } static inline void indentStream(std::ostream &str, unsigned depth) { for (unsigned d = 0; d < depth; d++) str << " "; } void SymbolGroupNode::dump(std::ostream &str, unsigned child, unsigned depth, bool humanReadable) const { const std::string iname = fullIName(); const std::string type = getType(); if (child) { // Separate list of children str << ','; if (humanReadable) str << '\n'; } if (humanReadable) indentStream(str, depth); str << "{iname=\"" << iname << "\",exp=\"" << iname << "\",name=\"" << m_name << "\",type=\"" << type << '"'; if (const ULONG64 addr = address()) str << ",addr=\"" << std::hex << std::showbase << addr << std::noshowbase << std::dec << '"'; bool valueEditable = true; bool valueEnabled = true; const bool uninitialized = m_flags & Uninitialized; if (uninitialized) { valueEditable = valueEnabled = false; str << ",valueencoded=\"0\",value=\"\""; } else { ULONG obtainedSize = 0; if (const wchar_t *wbuf = getValue(&obtainedSize)) { const ULONG valueSize = obtainedSize - 1; // ASCII or base64? if (isSevenBitClean(wbuf, valueSize)) { std::wstring value = wbuf; fixValue(type, &value); str << ",valueencoded=\"0\",value=\"" << gdbmiWStringFormat(value) << '"'; } else { str << ",valueencoded=\"2\",value=\""; base64Encode(str, reinterpret_cast(wbuf), valueSize * sizeof(wchar_t)); str << '"'; } delete [] wbuf; } } // Children: Dump all known or subelements (guess). const VectorIndexType childCountGuess = uninitialized ? 0 : (m_children.empty() ? m_parameters.SubElements : m_children.size()); // No children..suppose we are editable and enabled if (childCountGuess != 0) valueEditable = false; str << ",valueenabled=\"" << (valueEnabled ? "true" : "false") << '"' << ",valueeditable=\"" << (valueEditable ? "true" : "false") << '"' << ",numchild=\"" << childCountGuess << '"'; if (!uninitialized && !m_children.empty()) { str << ",children=["; if (humanReadable) str << '\n'; } } void SymbolGroupNode::dumpChildrenVisited(std::ostream &str, bool humanReadable) const { if (!m_children.empty()) str << ']'; str << '}'; if (humanReadable) str << '\n'; } bool SymbolGroupNode::accept(SymbolGroupNodeVisitor &visitor, unsigned child, unsigned depth) const { // If we happen to be the root node, just skip over const bool invisibleRoot = m_index == DEBUG_ANY_ID; const unsigned childDepth = invisibleRoot ? 0 : depth + 1; if (!invisibleRoot) { // Visit us and move index forward. if (visitor.visit(this, child, depth)) return true; } const unsigned childCount = unsigned(m_children.size()); for (unsigned c = 0; c < childCount; c++) if (m_children.at(c)->accept(visitor, c, childDepth)) return true; if (!invisibleRoot) visitor.childrenVisited(this, depth); return false; } void SymbolGroupNode::debug(std::ostream &str, unsigned verbosity, unsigned depth) const { indentStream(str, depth); str << '"' << fullIName() << "\",index=" << m_index; if (const VectorIndexType childCount = m_children.size()) str << ", Children=" << childCount; str << ' ' << m_parameters; if (m_flags) str << " node-flags=" << m_flags; if (verbosity) { str << ",name=\"" << m_name << "\", Address=0x" << std::hex << address() << std::dec << " Type=\"" << getType() << '"'; if (!(m_flags & Uninitialized)) str << "\" Value=\"" << gdbmiWStringFormat(rawValue()) << '"'; } str << '\n'; } // Expand! bool SymbolGroupNode::expand(std::string *errorMessage) { if (::debug > 1) DebugPrint() << "SymbolGroupNode::expand " << m_name << ' ' << m_index; if (!m_children.empty()) return true; if (m_parameters.SubElements == 0) { *errorMessage = "No subelements to expand in node: " + fullIName(); return false; } if (m_flags & Uninitialized) { *errorMessage = "Refusing to expand uninitialized node: " + fullIName(); return false; } const HRESULT hr = m_symbolGroup->debugSymbolGroup()->ExpandSymbol(m_index, TRUE); if (FAILED(hr)) { *errorMessage = msgDebugEngineComFailed("ExpandSymbol", hr); return false; } SymbolGroup::SymbolParameterVector parameters; // Retrieve parameters (including self, re-retrieve symbol parameters to get new 'expanded' flag // and corrected SubElement count (might be estimate)) if (!SymbolGroup::getSymbolParameters(m_symbolGroup->debugSymbolGroup(), m_index, m_parameters.SubElements + 1, ¶meters, errorMessage)) return false; // Before inserting children, correct indexes on whole group m_symbolGroup->root()->notifyExpanded(m_index + 1, parameters.at(0).SubElements); // Parse parameters, correct our own) and create child nodes. parseParameters(m_index, m_index, parameters); return true; } static inline std::string msgNotFound(const std::string &nodeName) { std::ostringstream str; str << "Node '" << nodeName << "' not found."; return str.str(); } std::string SymbolGroup::dump(bool humanReadable) const { std::ostringstream str; DumpSymbolGroupNodeVisitor visitor(str, humanReadable); if (humanReadable) str << '\n'; str << '['; accept(visitor); str << ']'; return str.str(); } // Dump a node, potentially expand std::string SymbolGroup::dump(const std::string &name, bool humanReadable, std::string *errorMessage) { SymbolGroupNode *const node = find(name); if (node == 0) { *errorMessage = msgNotFound(name); return std::string(); } if (node->subElements() && node->children().empty()) { if (!expand(name, errorMessage)) return false; } std::ostringstream str; if (humanReadable) str << '\n'; DumpSymbolGroupNodeVisitor visitor(str, humanReadable); str << '['; node->accept(visitor, 0, 0); str << ']'; return str.str(); } std::string SymbolGroup::debug(unsigned verbosity) const { std::ostringstream str; str << '\n'; DebugSymbolGroupNodeVisitor visitor(str, verbosity); accept(visitor); return str.str(); } /* expandList: Expand a list of inames with a 'mkdir -p'-like behaviour, that is, * expand all sub-paths. The list of inames has thus to be reordered to expand the * parent items first, for example "locals.this.i1.data,locals.this.i2" --->: * "locals, locals.this, locals.this.i1, locals.this.i2, locals.this.i1.data". * This is done here by creating a set of name parts keyed by level and name * (thus purging duplicates). */ typedef std::pair InamePathEntry; struct InamePathEntryLessThan : public std::binary_function { bool operator()(const InamePathEntry &i1, const InamePathEntry& i2) const { if (i1.first < i2.first) return true; if (i1.first != i2.first) return false; return i1.second < i2.second; } }; typedef std::set InamePathEntrySet; // Expand a node list "locals.i1,locals.i2" unsigned SymbolGroup::expandList(const std::vector &nodes, std::string *errorMessage) { if (nodes.empty()) return 0; // Create a set with a key . Also required for 1 node (see above). InamePathEntrySet pathEntries; const VectorIndexType nodeCount = nodes.size(); for (VectorIndexType i= 0; i < nodeCount; i++) { const std::string &iname = nodes.at(i); std::string::size_type pos = 0; for (unsigned level = 0; pos < iname.size(); level++) { std::string::size_type dotPos = iname.find('.', pos); if (dotPos == std::string::npos) dotPos = iname.size(); pathEntries.insert(InamePathEntry(level, iname.substr(0, dotPos))); pos = dotPos + 1; } } // Now expand going by level. unsigned succeeded = 0; std::string nodeError; InamePathEntrySet::const_iterator cend = pathEntries.end(); for (InamePathEntrySet::const_iterator it = pathEntries.begin(); it != cend; ++it) if (expand(it->second, &nodeError)) { succeeded++; } else { if (!errorMessage->empty()) errorMessage->append(", "); errorMessage->append(nodeError); } return succeeded; } bool SymbolGroup::expand(const std::string &nodeName, std::string *errorMessage) { SymbolGroupNode *node = find(nodeName); if (::debug) DebugPrint() << "expand: " << nodeName << " found=" << (node != 0) << '\n'; if (!node) { *errorMessage = msgNotFound(nodeName); return false; } if (node == m_root) // Shouldn't happen, still, all happy return true; return node->expand(errorMessage); } // Mark uninitialized (top level only) void SymbolGroup::markUninitialized(const std::vector &uniniNodes) { if (m_root && !m_root->children().empty() && !uniniNodes.empty()) { const std::vector::const_iterator unIniNodesBegin = uniniNodes.begin(); const std::vector::const_iterator unIniNodesEnd = uniniNodes.end(); const SymbolGroupNodePtrVector::const_iterator childrenEnd = m_root->children().end(); for (SymbolGroupNodePtrVector::const_iterator it = m_root->children().begin(); it != childrenEnd; ++it) { if (std::find(unIniNodesBegin, unIniNodesEnd, (*it)->fullIName()) != unIniNodesEnd) (*it)->setFlags((*it)->flags() | SymbolGroupNode::Uninitialized); } } } static inline std::string msgAssignError(const std::string &nodeName, const std::string &value, const std::string &why) { std::ostringstream str; str << "Unable to assign '" << value << "' to '" << nodeName << "': " << why; return str.str(); } bool SymbolGroup::assign(const std::string &nodeName, const std::string &value, std::string *errorMessage) { SymbolGroupNode *node = find(nodeName); if (node == 0) { *errorMessage = msgAssignError(nodeName, value, "No such node"); return false; } const HRESULT hr = m_symbolGroup->WriteSymbol(node->index(), const_cast(value.c_str())); if (FAILED(hr)) { *errorMessage = msgAssignError(nodeName, value, msgDebugEngineComFailed("WriteSymbol", hr)); return false; } return true; } bool SymbolGroup::accept(SymbolGroupNodeVisitor &visitor) const { if (!m_root || m_root->children().empty()) return false; return m_root->accept(visitor, 0, 0); } // Find "locals.this.i1" and move index recursively static SymbolGroupNode *findNodeRecursion(const std::vector &iname, unsigned depth, std::vector nodes) { typedef std::vector::const_iterator ConstIt; if (debug > 1) { DebugPrint() <<"findNodeRecursion: Looking for " << iname.back() << " (" << iname.size() << "),depth=" << depth << ",matching=" << iname.at(depth) << " in " << nodes.size(); } if (nodes.empty()) return 0; // Find the child that matches the iname part at depth const ConstIt cend = nodes.end(); for (ConstIt it = nodes.begin(); it != cend; ++it) { SymbolGroupNode *c = *it; if (c->iName() == iname.at(depth)) { if (depth == iname.size() - 1) { // Complete iname matched->happy. return c; } else { // Sub-part of iname matched. Forward index and check children. return findNodeRecursion(iname, depth + 1, c->children()); } } } return 0; } SymbolGroupNode *SymbolGroup::findI(const std::string &iname) const { if (iname.empty()) return 0; // Match the root element only: Shouldn't happen, still, all happy if (iname == m_root->name()) return m_root; std::vector inameTokens; split(iname, '.', std::back_inserter(inameTokens)); // Must begin with root if (inameTokens.front() != m_root->name()) return 0; // Start with index = 0 at root's children return findNodeRecursion(inameTokens, 1, m_root->children()); } SymbolGroupNode *SymbolGroup::find(const std::string &iname) const { SymbolGroupNode *rc = findI(iname); if (::debug > 1) DebugPrint() << "SymbolGroup::find " << iname << ' ' << rc; return rc; } // --------- DebugSymbolGroupNodeVisitor DebugSymbolGroupNodeVisitor::DebugSymbolGroupNodeVisitor(std::ostream &os, unsigned verbosity) : m_os(os), m_verbosity(verbosity) { } bool DebugSymbolGroupNodeVisitor::visit(const SymbolGroupNode *node, unsigned /* child */, unsigned depth) { node->debug(m_os, m_verbosity, depth); return false; } // --------------------- DumpSymbolGroupNodeVisitor DumpSymbolGroupNodeVisitor::DumpSymbolGroupNodeVisitor(std::ostream &os, bool humanReadable) : m_os(os), m_humanReadable(humanReadable) { } bool DumpSymbolGroupNodeVisitor::visit(const SymbolGroupNode *node, unsigned child, unsigned depth) { node->dump(m_os, child, depth, m_humanReadable); return false; } void DumpSymbolGroupNodeVisitor::childrenVisited(const SymbolGroupNode *node, unsigned) { node->dumpChildrenVisited(m_os, m_humanReadable); }