home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World Komputer 1999 mARCH
/
PCWK3A99.iso
/
Linux
/
DDD331
/
DDD-3_1_.000
/
DDD-3_1_
/
ddd-3.1.1
/
ddd
/
DispValue.C
< prev
next >
Wrap
C/C++ Source or Header
|
1998-11-26
|
36KB
|
1,559 lines
// $Id: DispValue.C,v 1.107 1998/11/26 10:21:01 zeller Exp $
// Read and store type and value of a displayed expression
// Copyright (C) 1995-1998 Technische Universitaet Braunschweig, Germany.
// Written by Dorothea Luetkehaus <luetke@ips.cs.tu-bs.de>
// and Andreas Zeller <zeller@ips.cs.tu-bs.de>.
//
// This file is part of DDD.
//
// DDD is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// DDD is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with DDD -- see the file COPYING.
// If not, write to the Free Software Foundation, Inc.,
// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
// DDD is the data display debugger.
// For details, see the DDD World-Wide-Web page,
// `http://www.cs.tu-bs.de/softech/ddd/',
// or send a mail to the DDD developers <ddd@ips.cs.tu-bs.de>.
char DispValue_rcsid[] =
"$Id: DispValue.C,v 1.107 1998/11/26 10:21:01 zeller Exp $";
#ifdef __GNUG__
#pragma implementation
#endif
#ifndef LOG_CREATE_VALUES
#define LOG_CREATE_VALUES 0
#endif
//-----------------------------------------------------------------------------
// A `DispValue' maintains type and value of a displayed expression
//-----------------------------------------------------------------------------
#include "DispValue.h"
#include "AppData.h"
#include "DispNode.h"
#include "DispValueA.h"
#include "DynArray.h"
#include "GDBAgent.h"
#include "PlotAgent.h"
#include "assert.h"
#include "cook.h"
#include "ddd.h"
#include "deref.h"
#include "fonts.h"
#include "isid.h"
#include "misc.h"
#include "plotter.h"
#include "question.h"
#include "regexps.h"
#include "string-fun.h"
#include "value-read.h"
#include <ctype.h>
//-----------------------------------------------------------------------------
// Helpers
//-----------------------------------------------------------------------------
int DispValue::cached_box_tics = 0;
StringStringAssoc DispValue::type_cache;
// Get index base of expr EXPR in dimension DIM
int DispValue::index_base(const string& expr, int dim)
{
if (gdb->program_language() != LANGUAGE_FORTRAN)
return gdb->default_index_base();
string base = expr;
if (base.contains('('))
base = base.before('(');
if (!type_cache.has(base))
type_cache[base] = gdb_question(gdb->whatis_command(base));
string type = type_cache[base];
// GDB issues array information as `type = real*8 (0:9,2:12)'.
// However, the first dimension in the type output comes last in
// the printed array.
int colon = type.length();
while (colon >= 0 && dim-- >= 0)
colon = type.index(':', colon - type.length() - 1);
if (colon < 0)
return gdb->default_index_base(); // Not found
while (colon >= 0 && isdigit(type[colon - 1]))
colon--;
return atoi((char *)type + colon);
}
// In FORTRAN mode, GDB issues last dimensions first. Insert new
// dimension before first dimension and convert to FORTRAN
// multi-dimension syntax.
string DispValue::add_member_name(const string& base,
const string& member_name)
{
if (gdb->program_language() == LANGUAGE_FORTRAN &&
member_name.contains('(', 0) && base.contains('('))
{
return base.before('(') + member_name.before(')') + ", " +
base.after('(');
}
return base + member_name;
}
void DispValue::clear_type_cache()
{
static StringStringAssoc empty;
type_cache = empty;
}
//-----------------------------------------------------------------------------
// Data
//-----------------------------------------------------------------------------
bool DispValue::expand_repeated_values = false;
DispValue *(*DispValue::value_hook)(string& value) = 0;
//-----------------------------------------------------------------------------
// Function defs
//-----------------------------------------------------------------------------
// Constructor
DispValue::DispValue (DispValue* parent,
int depth,
string& value,
const string& f_n,
const string& p_n,
DispValueType given_type)
: mytype(UnknownType), myexpanded(true), myenabled(true),
myfull_name(f_n), print_name(p_n), changed(false), myrepeats(1),
_value(""), _dereferenced(false), _children(0),
_index_base(0), _have_index_base(false), _alignment(Horizontal),
_has_plot_alignment(false), _plotter(0),
_cached_box(0), _cached_box_change(0),
_links(1)
{
init(parent, depth, value, given_type);
// A new display is not changed, but initialized
changed = false;
}
// Duplicator
DispValue::DispValue (const DispValue& dv)
: mytype(dv.mytype), myexpanded(dv.myexpanded),
myenabled(dv.myenabled), myfull_name(dv.myfull_name),
print_name(dv.print_name), myaddr(dv.myaddr),
changed(false), myrepeats(dv.myrepeats),
_value(dv.value()), _dereferenced(false), _children(dv.nchildren()),
_index_base(dv._index_base),
_have_index_base(dv._have_index_base), _alignment(dv._alignment),
_has_plot_alignment(false), _plotter(0),
_cached_box(0), _cached_box_change(0),
_links(1)
{
for (int i = 0; i < dv.nchildren(); i++)
{
_children += dv.child(i)->dup();
}
if (dv.cached_box() != 0)
set_cached_box(dv.cached_box()->link());
}
// True if more sequence members are coming
bool DispValue::sequence_pending(const string& value,
const DispValue *parent)
{
if (parent != 0)
{
switch (parent->type())
{
case Sequence:
case List:
case Struct:
case Reference:
case Array:
// In a composite, we always read everything up to the
// final delimiter.
return false;
case Simple:
case Pointer:
case Text:
case UnknownType:
break;
}
}
string v = value;
strip_leading_space(v);
if (v != "" && parent == 0)
return true; // Still more to read
if (!is_delimited(value))
return true; // Not at delimiter - more stuff follows
// Sequence is done
return false;
}
// In Perl, replace `{$REF}' and `$REF' by `REF->'
string DispValue::normalize_base(const string& base)
{
#if RUNTIME_REGEX
static regex rxsimple("([][a-zA-Z0-9_$@%().`]|->)*");
#endif
bool perl = gdb->program_language() == LANGUAGE_PERL;
string ref = base;
// Fetch one-letter Perl prefix
string prefix;
if (perl && ref != "" && is_perl_prefix(ref[0]))
{
prefix = ref[0];
ref = ref.after(0);
}
// Insert `->' operators
if (perl)
{
string r = ref;
if (r.contains("{", 0) && r.contains('}', -1))
r = unquote(r);
if (r.contains('$', 0) && r.matches(rxsimple))
{
if (r.contains("}", -1) || r.contains("]", -1))
ref = r.after(0); // Array between braces is optional
else
ref = r.after(0) + "->";
}
}
if (!perl)
{
// Place brackets around complex expressions
if (!ref.matches(rxsimple))
ref = "(" + ref + ")";
}
return prefix + ref;
}
// Parsing
DispValue *DispValue::parse(DispValue *parent,
int depth,
string& value,
const string& full_name,
const string& print_name,
DispValueType type)
{
if (value_hook != 0)
{
DispValue *dv = (*value_hook)(value);
if (dv != 0)
{
// Just take values from given element
#if LOG_CREATE_VALUES
clog << "External value " << quote(dv->full_name()) << "\n";
#endif
return dv;
}
}
return new DispValue(parent, depth, value, full_name, print_name, type);
}
// Initialization
void DispValue::init(DispValue *parent, int depth, string& value,
DispValueType given_type)
{
#if LOG_CREATE_VALUES
clog << "Building value from " << quote(value) << "\n";
#endif
// Be sure the value is not changed in memory
value.consuming(true);
char *initial_value = value;
static DispValueArray empty(0);
_children = empty;
if (background(value.length()))
{
clear();
mytype = Simple;
_value = "(Aborted)";
value = "Aborted\n";
return;
}
mytype = given_type;
if (mytype == UnknownType &&
(parent == 0 || parent->type() == List) && print_name == "")
mytype = Text;
if (mytype == UnknownType && parent == 0 && is_user_command(print_name))
mytype = List;
if (mytype == UnknownType)
mytype = determine_type(value);
bool ignore_repeats = (parent != 0 && parent->type() == Array);
char perl_type = '\0';
switch (mytype)
{
case Simple:
{
_value = read_simple_value(value, depth, ignore_repeats);
#if LOG_CREATE_VALUES
clog << mytype << ": " << quote(_value) << "\n";
#endif
perl_type = '$';
break;
}
case Text:
{
// Read in entire text
_value = value;
value = value.from(int(value.length())); // assigns ""
#if LOG_CREATE_VALUES
clog << mytype << ": " << quote(_value) << "\n";
#endif
perl_type = '$';
break;
}
case Pointer:
{
_value = read_pointer_value(value, ignore_repeats);
_dereferenced = false;
#if LOG_CREATE_VALUES
clog << mytype << ": " << quote(_value) << "\n";
#endif
// Hide vtable pointers.
if (_value.contains("virtual table") || _value.contains("vtable"))
myexpanded = false;
perl_type = '$';
// In Perl, pointers may be followed by indented `pointed to'
// info. Skip this.
if (gdb->type() == PERL)
{
while (value.contains("\n ", 0))
{
value = value.after("\n ");
value = value.from("\n");
}
}
break;
}
case Array:
{
string base = normalize_base(myfull_name);
_alignment = Vertical;
#if LOG_CREATE_VALUES
clog << mytype << ": " << "\n";
#endif
read_array_begin(value, myaddr);
// Check for `vtable entries' prefix.
string vtable_entries = read_vtable_entries(value);
if (vtable_entries != "")
{
_children += parse_child(depth, vtable_entries, myfull_name);
}
// Read the array elements. Assume that the type is the
// same across all elements.
DispValueType member_type = UnknownType;
if (!_have_index_base)
{
_index_base = index_base(base, depth);
_have_index_base = true;
}
int array_index = _index_base;
// The array has at least one element. Otherwise, GDB
// would treat it as a pointer.
do {
char *repeated_value = value;
string member_name =
gdb->index_expr("", itostring(array_index++));
DispValue *dv = parse_child(depth, value,
add_member_name(base, member_name),
member_name, member_type);
member_type = dv->type();
_children += dv;
int repeats = read_repeats(value);
if (expand_repeated_values)
{
// Create one value per repeat
while (--repeats > 0)
{
member_name =
gdb->index_expr("", itostring(array_index++));
string val = repeated_value;
DispValue *repeated_dv =
parse_child(depth, val,
add_member_name(base, member_name),
member_name, member_type);
_children += repeated_dv;
}
}
else
{
// Show repetition in member
if (repeats > 1)
{
array_index--;
#if 0
// We use the GDB `artificial array' notation here,
// since repeat recognition is supported in GDB only.
member_name += "@" + itostring(repeats);
dv->full_name() = add_member_name(base, member_name);
dv->name() = member_name;
#endif
dv->repeats() = repeats;
array_index += repeats;
}
}
if (background(value.length()))
{
init(parent, depth, value);
return;
}
} while (read_array_next(value));
read_array_end(value);
// Expand only if at top-level.
myexpanded = (depth == 0 || nchildren() <= 1);
#if LOG_CREATE_VALUES
clog << mytype << " has " << nchildren() << " members\n";
#endif
perl_type = '@';
break;
}
case List:
// Some DBXes issue the local variables via a frame line, just
// like `set_date(d = 0x10003060, day_of_week = Sat, day = 24,
// month = 12, year = 1994)'. Make this more readable.
munch_dump_line(value);
// FALL THROUGH
case Struct:
{
bool found_struct_begin = false;
bool read_multiple_values = false;
#if LOG_CREATE_VALUES
clog << mytype << " " << quote(myfull_name) << "\n";
#endif
string member_prefix = myfull_name;
string member_suffix = "";
if (mytype == List)
{
member_prefix = "";
read_multiple_values = true;
}
else
{
// In C and Java, `*' binds tighter than `.'
if (member_prefix.contains('*', 0))
{
if (gdb->program_language() == LANGUAGE_C)
{
// Use the C `->' operator instead
member_prefix.del("*");
if (member_prefix.contains('(', 0) &&
member_prefix.contains(')', -1))
member_prefix = unquote(member_prefix);
#if RUNTIME_REGEX
static regex rxchain("[-a-zA-Z0-9::_>.`]+");
#endif
if (member_prefix.matches(rxchain))
{
// Simple chain of identifiers - prepend `->'
member_prefix += "->";
}
else
{
member_prefix.prepend("(");
member_prefix += ")->";
}
}
else
{
member_prefix.prepend("(");
member_prefix += ").";
}
}
else if (gdb->program_language() == LANGUAGE_PERL)
{
// In Perl, members of A are accessed as A{...}
member_prefix = normalize_base(member_prefix) + "{";
member_suffix = "}";
}
else
{
// In all other languages, members are accessed as A.B
member_prefix = normalize_base(member_prefix) + ".";
}
// In case we do not find a struct beginning, read only one value
found_struct_begin = read_struct_begin(value, myaddr);
read_multiple_values = found_struct_begin;
}
bool more_values = true;
while (more_values)
{
string member_name = read_member_name(value);
if (member_name == "")
{
// Some struct stuff that is not a member
char *old_value = value;
DispValue *dv = parse_child(depth, value, myfull_name, "");
bool consume = true;
if (value == old_value)
{
// Nothing consumed - stop here
consume = false;
}
else if (dv->type() == Simple && dv->value() == "")
{
// Empty value - stop here
consume = false;
}
else if (dv->type() == Struct)
{
// What's this - a struct within a struct? Just
// adopt the members.
// (This happens when we finally found the struct
// after having read all the AIX DBX base classes.)
for (int i = 0; i < dv->nchildren(); i++)
{
DispValue *dv2 = dv->child(i)->link();
_children += dv2;
}
consume = false;
}
if (!consume)
{
// Discard the value just read
dv->unlink();
}
else
{
_children += dv;
}
more_values = read_multiple_values && read_struct_next(value);
}
else if (is_BaseClass_name(member_name))
{
// Base class member
DispValue *dv =
parse_child(depth, value, myfull_name, member_name);
_children += dv;
more_values = read_multiple_values && read_struct_next(value);
// Skip a possible `members of CLASS:' prefix
read_members_prefix(value);
// AIX DBX does not place a separator between base
// classes and the other members, so we always
// continue reading after having found a base
// class. After all, the own class members are
// still missing.
if (mytype == Struct && !found_struct_begin)
more_values = true;
}
else
{
// Ordinary member
string full_name;
if (member_name == " ")
{
// Anonymous union
full_name = myfull_name;
}
else if (member_name.contains('.') && gdb->has_quotes())
{
// The member name contains `.' => quote it. This
// happens with vtable pointers on Linux (`_vptr.').
full_name = member_prefix + quote(member_name, '\'') +
member_suffix;
}
else
{
// Ordinary member
full_name = member_prefix + member_name + member_suffix;
}
_children += parse_child(depth, value, full_name, member_name);
more_values = read_multiple_values && read_struct_next(value);
}
if (background(value.length()))
{
init(parent, depth, value);
return;
}
}
if (mytype == List && value != "")
{
// Add remaining value as text
_children += parse_child(depth, value, "");
}
if (found_struct_begin)
{
// Skip the remainder
read_struct_end(value);
}
// Expand only if at top-level.
myexpanded = (depth == 0 || nchildren() <= 1);
#if LOG_CREATE_VALUES
clog << mytype << " "
<< quote(myfull_name)
<< " has " << nchildren() << " members\n";
#endif
perl_type = '%';
break;
}
case Reference:
{
myexpanded = true;
int sep = value.index('@');
sep = value.index(':', sep);
string ref = value.before(sep);
value = value.after(sep);
string addr = gdb->address_expr(myfull_name);
_children += parse_child(depth, ref, addr, myfull_name, Pointer);
_children += parse_child(depth, value, myfull_name);
if (background(value.length()))
{
init(parent, depth, value);
return;
}
perl_type = '$'; // No such thing in Perl...
break;
}
case Sequence:
case UnknownType:
assert(0);
abort();
}
// Handle trailing stuff (`sequences')
if (parent == 0 || parent->type() != Sequence)
{
bool need_clear = true;
while (sequence_pending(value, parent))
{
if (need_clear)
{
#if LOG_CREATE_VALUES
clog << "Sequence detected at " << quote(value) << "\n";
#endif
clear();
value = initial_value;
mytype = Sequence;
#if LOG_CREATE_VALUES
clog << mytype << " " << quote(myfull_name) << "\n";
#endif
need_clear = false;
}
char *old_value = value;
DispValue *dv = parse_child(depth, value, myfull_name);
if (value == old_value)
{
// Nothing consumed - stop here
dv->unlink();
break;
}
else if (dv->type() == Simple && dv->value() == "")
{
// Empty value - ignore
dv->unlink();
}
else
{
_children += dv;
}
}
#if LOG_CREATE_VALUES
if (!need_clear)
{
clog << mytype << " "
<< quote(myfull_name)
<< " has " << nchildren() << " members\n";
}
#endif
}
if (gdb->program_language() == LANGUAGE_PERL && is_perl_prefix(perl_type))
{
// Set new type
if (myfull_name != "" && is_perl_prefix(myfull_name[0]))
myfull_name[0] = perl_type;
}
background(value.length());
changed = true;
}
// Destructor helper
void DispValue::clear()
{
for (int i = 0; i < nchildren(); i++)
child(i)->unlink();
static DispValueArray empty(0);
_children = empty;
if (plotter() != 0)
{
plotter()->terminate();
_plotter = 0;
}
clear_cached_box();
}
//-----------------------------------------------------------------------------
// Cache
//-----------------------------------------------------------------------------
void DispValue::validate_box_cache()
{
for (int i = 0; i < nchildren(); i++)
{
child(i)->validate_box_cache();
if (child(i)->cached_box() == 0 ||
child(i)->_cached_box_change > _cached_box_change)
{
clear_cached_box();
break;
}
}
}
//-----------------------------------------------------------------------------
// Resources
//-----------------------------------------------------------------------------
// Return dereferenced name. Only if type() == Pointer.
string DispValue::dereferenced_name() const
{
switch (mytype)
{
case Pointer:
{
string f = full_name();
if (f.contains('/', 0))
f = f.from(2); // Skip /FMT expressions
return deref(f);
}
case Simple:
case Text:
case Array:
case Sequence:
case List:
case Struct:
case Reference:
return "";
case UnknownType:
assert(0);
abort();
}
return "";
}
//-----------------------------------------------------------------------------
// Modifiers
//-----------------------------------------------------------------------------
// Expand. Like expand(), but expand entire subtree
void DispValue::expandAll(int depth)
{
if (depth == 0)
return;
_expand();
for (int i = 0; i < nchildren(); i++)
{
child(i)->expandAll(depth - 1);
}
}
// Collapse. Like collapse(), but collapse entire subtree
void DispValue::collapseAll(int depth)
{
if (depth == 0)
return;
_collapse();
for (int i = 0; i < nchildren(); i++)
{
child(i)->collapseAll(depth - 1);
}
}
// Count expanded nodes in tree
int DispValue::expandedAll() const
{
int count = 0;
if (expanded())
count++;
for (int i = 0; i < nchildren(); i++)
count += child(i)->expandedAll();
return count;
}
// Count collapsed nodes in tree
int DispValue::collapsedAll() const
{
int count = 0;
if (collapsed())
count++;
for (int i = 0; i < nchildren(); i++)
count += child(i)->collapsedAll();
return count;
}
// Return height of entire tree
int DispValue::height() const
{
int d = 0;
for (int i = 0; i < nchildren(); i++)
d = max(d, child(i)->height());
return d + 1;
}
// Return height of expanded tree
int DispValue::heightExpanded() const
{
if (collapsed())
return 0;
int d = 0;
for (int i = 0; i < nchildren(); i++)
{
if (child(i)->collapsed())
return 1;
d = max(d, child(i)->heightExpanded());
}
return d + 1;
}
void DispValue::set_alignment(DispValueAlignment alignment)
{
if (_alignment == alignment)
return;
_alignment = alignment;
clear_cached_box();
if (type() == Simple && plotter() != 0)
plot();
}
//-----------------------------------------------------------------------------
// Update values
//-----------------------------------------------------------------------------
// Update values from VALUE. Set WAS_CHANGED iff value changed; Set
// WAS_INITIALIZED iff type changed. If TYPE is given, use TYPE as
// type instead of inferring it. Note: THIS can no more be referenced
// after calling this function; use the returned value instead.
DispValue *DispValue::update(string& value,
bool& was_changed, bool& was_initialized,
DispValueType given_type)
{
DispValue *source = parse(0, 0, value,
full_name(), name(), given_type);
if (background(value.length()))
{
// Aborted while parsing - use SOURCE instead of original
DispValue *ret = source->link();
ret->changed = was_changed = was_initialized = true;
unlink();
return ret;
}
DispValue *dv = update(source, was_changed, was_initialized);
source->unlink();
if (was_changed || was_initialized)
clear_cached_box();
return dv;
}
// Update values from SOURCE. Set WAS_CHANGED iff value changed; Set
// WAS_INITIALIZED iff type changed. Note: Neither THIS nor SOURCE
// can be referenced after calling this function; use the returned
// value instead.
DispValue *DispValue::update(DispValue *source,
bool& was_changed, bool& was_initialized)
{
bool was_plotted = (plotter() != 0);
DispValue *dv = _update(source, was_changed, was_initialized);
if (was_plotted && was_changed)
dv->plot();
return dv;
}
DispValue *DispValue::_update(DispValue *source,
bool& was_changed, bool& was_initialized)
{
if (source == this)
{
// We're updated from ourselves -- ignore it all.
// This happens when a cluster is updated from the values of
// the clustered dislays.
if (descendant_changed())
was_changed = true;
return this;
}
if (changed)
{
// Clear `changed' flag
changed = false;
was_changed = true;
}
if (source->enabled() != enabled())
{
myenabled = source->enabled();
was_changed = true;
// We don't set CHANGED to true since enabled/disabled changes
// are merely a change in the view, not a change in the data.
}
if (source->full_name() == full_name() && source->type() == type())
{
switch (type())
{
case Simple:
case Text:
case Pointer:
// Atomic values
if (_value != source->value())
{
_value = source->value();
changed = was_changed = true;
}
return this;
case Array:
// Array. Check for 1st element, too.
if (_have_index_base != source->_have_index_base &&
(_have_index_base && _index_base != source->_index_base))
break;
// FALL THROUGH
case Reference:
case Sequence:
// Numbered children. If size changed, we assume
// the whole has been changed.
if (nchildren() == source->nchildren())
{
for (int i = 0; i < nchildren(); i++)
{
// Update each child
_children[i] = child(i)->update(source->child(i),
was_changed,
was_initialized);
}
return this;
}
break;
case List:
case Struct:
{
// Named children. Check whether names are the same.
bool same_members = (nchildren() == source->nchildren());
for (int i = 0; same_members && i < nchildren(); i++)
{
if (child(i)->full_name() != source->child(i)->full_name())
same_members = false;
}
if (same_members)
{
// Update each child
for (int i = 0; i < nchildren(); i++)
{
_children[i] = child(i)->update(source->child(i),
was_changed,
was_initialized);
}
return this;
}
// Members have changed.
// Be sure to mark only those members that actually have changed
// (i.e. don't mark the entire struct and don't mark new members)
// We do so by creating a new list of children. `Old' children
// that still are reported get updated; `new' children are added.
DispValueArray new_children;
DispValueArray processed_children;
for (int j = 0; j < source->nchildren(); j++)
{
DispValue *c = 0;
for (int i = 0; c == 0 && i < nchildren(); i++)
{
bool processed = false;
for (int k = 0; k < processed_children.size(); k++)
{
if (child(i) == processed_children[k])
processed = true;
}
if (processed)
continue;
if (child(i)->full_name() == source->child(j)->full_name())
{
c = child(i)->update(source->child(j),
was_changed,
was_initialized);
processed_children += child(i);
}
}
if (c == 0)
{
// Child not found -- use source child
c = source->child(j)->link();
}
new_children += c;
}
_children = new_children;
was_changed = was_initialized = true;
return this;
}
case UnknownType:
assert(0);
abort();
}
}
// Type, name or structure have changed -- use SOURCE instead of original
DispValue *ret = source->link();
ret->changed = was_changed = was_initialized = true;
// Copy the basic settings
ret->myexpanded = expanded();
ret->dereference(dereferenced());
if (vertical_aligned())
ret->align_vertical();
if (horizontal_aligned())
ret->align_horizontal();
unlink();
return ret;
}
// Return true iff this or some descendant changed
bool DispValue::descendant_changed() const
{
if (changed)
return true;
for (int i = 0; i < nchildren(); i++)
if (child(i)->descendant_changed())
return true;
return false;
}
//-----------------------------------------------------------------------------
// Find descendant
//-----------------------------------------------------------------------------
// Return true iff SOURCE and this are structurally equal.
// If SOURCE_DESCENDANT (a descendant of SOURCE) is set,
// return its equivalent descendant of this in DESCENDANT.
bool DispValue::structurally_equal(const DispValue *source,
const DispValue *source_descendant,
const DispValue *&descendant) const
{
if (source == source_descendant)
descendant = this;
if (type() != source->type())
return false; // Differing type
switch (type())
{
case Simple:
case Text:
case Pointer:
return true; // Structurally equal
case Array:
{
if (nchildren() != source->nchildren())
return false; // Differing size
if (_have_index_base != source->_have_index_base)
return false; // Differing base
if (_have_index_base && _index_base != source->_index_base)
return false; // Differing base
for (int i = 0; i < nchildren(); i++)
{
DispValue *child = _children[i];
DispValue *source_child = source->child(i);
bool eq = child->structurally_equal(source_child,
source_descendant,
descendant);
if (!eq)
return false;
}
return true; // All children structurally equal
}
case List:
case Struct:
case Sequence:
case Reference:
{
if (nchildren() != source->nchildren())
return false;
for (int i = 0; i < nchildren(); i++)
{
DispValue *child = _children[i];
DispValue *source_child = source->child(i);
bool eq = child->structurally_equal(source_child,
source_descendant,
descendant);
if (!eq)
return false;
}
return true; // All children structurally equal
}
case UnknownType:
assert(0);
abort();
}
return false; // Not found
}
//-----------------------------------------------------------------------------
// Plotting
//-----------------------------------------------------------------------------
int DispValue::can_plot() const
{
if (can_plot3d())
return 3;
if (can_plot2d())
return 2;
if (can_plot1d())
return 1;
// Search for plottable array children
int ndim = 0;
for (int i = 0; i < nchildren(); i++)
ndim = max(ndim, child(i)->can_plot());
return ndim;
}
bool DispValue::starts_number(char c)
{
return c == '.' || c == '+' || c == '-' || isdigit(c);
}
bool DispValue::can_plot1d() const
{
if (type() != Simple)
return false;
const string& v = value();
if (v.length() == 0)
return false; // Empty value
if (!starts_number(v[0]))
return false; // Not a numeric value
return true;
}
bool DispValue::can_plot2d() const
{
if (type() != Array)
return false;
for (int i = 0; i < nchildren(); i++)
{
if (!child(i)->can_plot1d())
return false;
}
return true;
}
bool DispValue::can_plot3d() const
{
if (type() != Array)
return false;
int grandchildren = -1;
for (int i = 0; i < nchildren(); i++)
{
if (!child(i)->can_plot2d())
return false;
if (i == 0)
grandchildren = child(i)->nchildren_with_repeats();
else if (child(i)->nchildren_with_repeats() != grandchildren)
return false; // Differing number of grandchildren
}
return true;
}
int DispValue::nchildren_with_repeats() const
{
int sum = 0;
for (int i = 0; i < nchildren(); i++)
sum += child(i)->repeats();
return sum;
}
// Return a title for NAME
string DispValue::make_title(const string& name)
{
if (!is_user_command(name))
return name;
string title = user_command(name);
if (title.contains("graph ", 0))
title = title.after("graph ");
else if (title.contains("info ", 0))
title = title.after("info ");
else if (title.contains(" "))
title = title.before(" ");
if (title.length() > 0)
title = toupper(title[0]) + title.after(0);
return title;
}
void DispValue::plot() const
{
int ndim = can_plot();
if (ndim == 0)
return;
if (plotter() == 0)
{
string title = make_title(full_name());
((DispValue *)this)->_plotter = new_plotter(title, (DispValue *)this);
if (plotter() == 0)
return;
plotter()->addHandler(Died, PlotterDiedHP, (void *)this);
}
plotter()->plot_2d_settings = app_data.plot_2d_settings;
plotter()->plot_3d_settings = app_data.plot_3d_settings;
_plot(plotter(), ndim);
plotter()->flush();
}
void DispValue::_plot(PlotAgent *plotter, int ndim) const
{
if (can_plot3d())
{
plot3d(plotter, ndim);
return;
}
if (can_plot2d())
{
plot2d(plotter, ndim);
return;
}
if (can_plot1d())
{
plot1d(plotter, ndim);
return;
}
// Plot all array children into one window
for (int i = 0; i < nchildren(); i++)
child(i)->_plot(plotter, ndim);
}
void DispValue::replot() const
{
if (plotter() != 0)
plot();
for (int i = 0; i < nchildren(); i++)
child(i)->replot();
}
string DispValue::num_value() const
{
const string& v = value();
if (v.contains(rxdouble, 0))
return v.through(rxdouble);
if (v.contains(rxint, 0))
return v.through(rxint);
return "0"; // Illegal value
}
void DispValue::plot1d(PlotAgent *plotter, int ndim) const
{
plotter->start_plot(full_name(), ndim);
string val = num_value();
if (!has_plot_alignment())
{
// Determine initial alignment.
// By default, this is plotted horizontally.
DispValueAlignment alignment = Horizontal;
// But if this is an integral value that lies within the index
// limits of a previously plotted array, plot it vertically.
if (!val.contains('.'))
{
int v = atoi(val);
if (plotter->min_x() < plotter->max_x() &&
v >= plotter->min_x() && v <= plotter->max_x())
{
alignment = Vertical;
}
}
((DispValue *)this)->_alignment = alignment;
((DispValue *)this)->_has_plot_alignment = true;
}
plotter->add_point(val, horizontal_aligned() ? 0 : 1);
plotter->end_plot();
}
void DispValue::plot2d(PlotAgent *plotter, int ndim) const
{
plotter->start_plot(full_name(), ndim);
int index;
if (_have_index_base)
index = _index_base;
else
index = gdb->default_index_base();
for (int i = 0; i < nchildren(); i++)
{
DispValue *c = child(i);
for (int ii = 0; ii < c->repeats(); ii++)
{
plotter->add_point(index++, c->num_value());
}
}
plotter->end_plot();
}
void DispValue::plot3d(PlotAgent *plotter, int ndim) const
{
plotter->start_plot(full_name(), ndim);
int index;
if (_have_index_base)
index = _index_base;
else
index = gdb->default_index_base();
for (int i = 0; i < nchildren(); i++)
{
DispValue *c = child(i);
int c_index;
if (c->_have_index_base)
c_index = c->_index_base;
else
c_index = gdb->default_index_base();
for (int ii = 0; ii < c->repeats(); ii++)
{
for (int j = 0; j < c->nchildren(); j++)
{
DispValue *cc = c->child(j);
for (int jj = 0; jj < cc->repeats(); jj++)
plotter->add_point(index, c_index++, cc->num_value());
}
index++;
}
plotter->add_break();
}
plotter->end_plot();
}
void DispValue::PlotterDiedHP(Agent *source, void *client_data, void *)
{
(void) source; // Use it
DispValue *dv = (DispValue *)client_data;
assert(source == dv->plotter());
dv->plotter()->removeHandler(Died, PlotterDiedHP, (void *)dv);
dv->_plotter = 0;
}
// Print plots to FILENAME
void DispValue::print_plots(const string& filename,
const PrintGC& gc) const
{
// Print self
if (plotter() != 0)
plotter()->print(filename, gc);
// Print children
for (int i = 0; i < nchildren(); i++)
child(i)->print_plots(filename, gc);
}
// Show whether plot is active
void DispValue::set_plot_state(const string& state) const
{
// Handle self
if (plotter() != 0)
plotter()->set_state(state);
// Handle children
for (int i = 0; i < nchildren(); i++)
child(i)->set_plot_state(state);
}
//-----------------------------------------------------------------------------
// Background processing
//-----------------------------------------------------------------------------
static bool nop(int) { return false; }
bool (*DispValue::background)(int processed) = nop;