Logo Search packages:      
Sourcecode: ddd version File versions  Download package

DispValue.C

// $Id$
// Read and store type and value of a displayed expression

// Copyright (C) 1995-1998 Technische Universitaet Braunschweig, Germany.
// Copyright (C) 2000-2001 Universitaet Passau, Germany.
// Written by Dorothea Luetkehaus <luetke@ips.cs.tu-bs.de>
// and Andreas Zeller <zeller@gnu.org>.
// 
// 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.gnu.org/software/ddd/',
// or send a mail to the DDD developers <ddd@gnu.org>.

char DispValue_rcsid[] =
    "$Id$";

#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(type.chars() + 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), myaddr(""), 
      changed(false), myrepeats(1),
      _value(""), _dereferenced(false), _member_names(true), _children(0),
      _index_base(0), _have_index_base(false), _orientation(Horizontal),
      _has_plot_orientation(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), 
      _member_names(dv.member_names()), _children(dv.nchildren()), 
       _index_base(dv._index_base), 
      _have_index_base(dv._have_index_base), _orientation(dv._orientation),
      _has_plot_orientation(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.empty() && 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) const
{
#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.empty() && 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
          std::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
    std::clog << "Building value from " << quote(value) << "\n";
#endif

    // Be sure the value is not changed in memory
    value.consuming(true);

    const char *initial_value = value.chars();

    static const 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.empty())
      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
      std::clog << mytype << ": " << quote(_value) << "\n";
#endif
      perl_type = '$';
      break;
    }

    case Text:
    {
      // Read in a line of text
      if (value.contains('\n'))
          _value = value.through('\n');
      else
          _value = value;
      value = value.after('\n');
#if LOG_CREATE_VALUES
      std::clog << mytype << ": " << quote(_value) << "\n";
#endif
      perl_type = '$';
      break;
    }

    case Pointer:
    {
      _value = read_pointer_value(value, ignore_repeats);
      _dereferenced = false;

#if LOG_CREATE_VALUES
      std::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);

      _orientation = app_data.array_orientation;

#if LOG_CREATE_VALUES
      std::clog << mytype << ": " << "\n";
#endif

      read_array_begin(value, myaddr);

      // Check for `vtable entries' prefix.
      string vtable_entries = read_vtable_entries(value);
      if (!vtable_entries.empty())
      {
          _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 {
          const char *repeated_value = value.chars();
          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
      std::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:
    {
      _orientation  = app_data.struct_orientation;
      _member_names = app_data.show_member_names;

      bool found_struct_begin   = false;
      bool read_multiple_values = false;
      
#if LOG_CREATE_VALUES
      std::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_NAME'}
            member_prefix = normalize_base(member_prefix) + "{'";
            member_suffix = "'}";
          }
          else if (gdb->program_language() == LANGUAGE_PHP)
          {
            // In PHP, members of $A are accessed as $A['MEMBER_NAME']
            member_prefix = normalize_base(member_prefix) + "['";
            member_suffix = "']";
          }
          else if (gdb->program_language() == LANGUAGE_FORTRAN)
          {
            // In Fortran, members of A are accessed as A%B
            member_prefix = normalize_base(member_prefix) + "%";
          }
          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;
      }

      // Prepend base class in case of multiple inheritance
      // FIXME: This should be passed as an argument
      static string baseclass_prefix;
      member_prefix += baseclass_prefix;
      int base_classes = 0;

      bool more_values = true;
      while (more_values)
      {
          // In a List, we may have `member' names like `(a + b)'.
          // Don't be picky about this.
          bool picky = (mytype == Struct);
          string member_name = read_member_name(value, picky);

          if (member_name.empty())
          {
            // Some struct stuff that is not a member
            DispValue *dv = parse_child(depth, value, myfull_name, "");

            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;
                }
                dv->unlink();
            }
            else
            {
                _children += dv;
            }

            more_values = read_multiple_values && read_struct_next(value);
          }
          else if (is_BaseClass_name(member_name))
          {
            // Base class member
            string saved_baseclass_prefix = baseclass_prefix;
            base_classes++;

            if (base_classes > 1)
            {
                // Multiple inheritance.  Be sure to
                // reference further members unambiguously.
                //
                // Note: we don't do that for the first base class,
                // because this might turn ambiguous again.
                //
                // Example:
                //
                //    Base
                //    |   |
                //    I1 I2
                //     \ /
                //      C
                //
                // Members of I1::Base are not prefixed, members
                // of I2::Base get `I2::' as base class prefix.
                // If we did this already for the first base class,
                // members of both I1 and I2 would get `Base::' as
                // base class prefix.

                switch (gdb->program_language())
                {
                case LANGUAGE_C: // C++
                  baseclass_prefix = unquote(member_name) + "::";
                  break;

                default:
                  // Do nothing (yet)
                  break;
                }
            }

            DispValue *dv = 
                parse_child(depth, value, myfull_name, member_name);
            _children += dv;

            baseclass_prefix = saved_baseclass_prefix;

            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;
            }
            
            if (member_name.contains('.'))
            {
                if (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
                {
                  // JDB (and others?) prepend the class name 
                  // to inherited members.  Omit this.
                  full_name = 
                      member_prefix + member_name.after('.', -1) + 
                      member_suffix;
                }
            }
            
            if (full_name.empty())
            {
                // Ordinary member
                full_name = member_prefix + member_name + member_suffix;
            }

            DispValue *child = 
                parse_child(depth, value, full_name, member_name);

            if (child->type() == Text)
            {
                // Found a text as child - child value must be empty
                string empty = "";
                _children += 
                  parse_child(depth, empty, full_name, member_name);

                string v = child->value();
                strip_space(v);
                if (!v.empty())
                  _children += child;
            }
            else
            {
                _children += child;
            }

            more_values = read_multiple_values && read_struct_next(value);
          }

          if (background(value.length()))
          {
            init(parent, depth, value);
            return;
          }
      }

      if (mytype == List && !value.empty())
      {
          // 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
      std::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
            std::clog << "Sequence detected at " << quote(value) << "\n";
#endif

            clear();
            value = initial_value;

            mytype = Sequence;

#if LOG_CREATE_VALUES
            std::clog << mytype << " " << quote(myfull_name) << "\n";
#endif

            need_clear = false;
          }
          
          const char *old_value = value.chars();

          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())
          {
            // Empty value - ignore
            dv->unlink();
          }
          else
          {
            _children += dv;
          }
      }

#if LOG_CREATE_VALUES
      if (!need_clear)
      {
          std::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.empty() && 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 const DispValueArray empty(0);
    _children = empty;

    if (plotter() != 0)
    {
      plotter()->terminate();
      _plotter = 0;
    }

    clear_cached_box();
}

//-----------------------------------------------------------------------------
// Cache
//-----------------------------------------------------------------------------

void DispValue::validate_box_cache()
{
    int i;
    for (i = 0; i < nchildren(); i++)
      child(i)->validate_box_cache();

    for (i = 0; i < nchildren(); i++)
    {
      if (child(i)->cached_box() == 0 ||
          child(i)->_cached_box_change > _cached_box_change)
      {
          clear_cached_box();
          break;
      }
    }
}

// Clear box caches for this and all children
void DispValue::clear_box_cache()
{
    clear_cached_box();

    for (int i = 0; i < nchildren(); i++)
      child(i)->clear_box_cache();
}

//-----------------------------------------------------------------------------
// 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_orientation(DispValueOrientation orientation)
{
    if (_orientation == orientation)
      return;

    _orientation = orientation;
    clear_cached_box();

    if (type() == Simple && plotter() != 0)
      plot();

    // Save orientation for next time
    switch (type())
    {
    case Array:
      app_data.array_orientation = orientation;
      break;

    case List:
    case Struct:
      app_data.struct_orientation = orientation;
      break;

    default:
      break;
    }
}

void DispValue::set_member_names(bool value)
{
    if (_member_names == value)
      return;

    _member_names = value;
    clear_cached_box();

    // Save setting for next time
    app_data.show_member_names = value;
}


//-----------------------------------------------------------------------------
// 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;

      // Have the new DispValue take over the plotter
      if (ret->plotter() == 0)
      {
          ret->_plotter = plotter();
          _plotter = 0;
      }

      unlink();
      return ret;
    }

    DispValue *dv = update(source, was_changed, was_initialized);

    if (was_changed || was_initialized)
      dv->clear_cached_box();

    source->unlink();

    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)
{
    assert(source->OK());

    bool was_plotted = (plotter() != 0);

    DispValue *dv = _update(source, was_changed, was_initialized);
    assert(dv->OK());

    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 instead
                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());
    ret->set_orientation(orientation());
    ret->set_member_names(member_names());

    // Have new DispValue take over the plotter
    if (ret->plotter() == 0)
    {
      ret->_plotter = plotter();
      _plotter = 0;
    }

    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;
}

// If the names of all children have the form (PREFIX)(INDEX)(SUFFIX),
// return the common PREFIX and SUFFIX.
void DispValue::get_index_surroundings(string& prefix, string& suffix) const
{
    assert (nchildren() > 0);

    prefix = child(0)->full_name();
    suffix = child(0)->full_name();

    for (int i = 1; i < nchildren(); i++)
    {
      prefix = common_prefix(prefix, child(i)->full_name());
      suffix = common_suffix(suffix, child(i)->full_name());
    }
}

// If the name has the form (PREFIX)(INDEX)(SUFFIX), return INDEX
string DispValue::index(const string& prefix, const string& suffix) const
{
    string idx = full_name();
    idx = idx.from(int(prefix.length()));
    idx = idx.before(int(idx.length() - suffix.length()));
    strip_space(idx);

    return idx;
}

bool DispValue::can_plot2d() const
{
    if (type() == Array)
    {
      for (int i = 0; i < nchildren(); i++)
      {
          if (!child(i)->can_plot1d())
            return false;
      }

      return true;
    }

    if (nchildren() > 0)
    {
      // If we have a list of indexed names, then we can plot in 2d.
      int i;
      string prefix, suffix;
      get_index_surroundings(prefix, suffix);
      
      for (i = 0; i < nchildren(); i++)
      {
          string idx = child(i)->index(prefix, suffix);
          if (!idx.matches(rxdouble) && !idx.matches(rxint))
            return false;
      }

      for (i = 0; i < nchildren(); i++)
      {
          if (!child(i)->can_plot1d())
            return false;
      }

      return true;
    }

    return false;
}

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(CLUSTER_COMMAND " ", 0))
      return title.after(CLUSTER_COMMAND " ");

    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());
      MUTABLE_THIS(DispValue *)->_plotter = new_plotter(title, CONST_CAST(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(make_title(full_name()), ndim);

    string val = num_value();

    if (!has_plot_orientation())
    {
      // Determine initial orientation.
      // By default, this is plotted horizontally.
      DispValueOrientation orientation = 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.chars());
          if (plotter->min_x() < plotter->max_x() &&
            v >= plotter->min_x() && v <= plotter->max_x())
          {
            orientation = Vertical;
          }
      }
      
      MUTABLE_THIS(DispValue *)->_orientation = orientation;
      MUTABLE_THIS(DispValue *)->_has_plot_orientation = true;
    }

    plotter->add_point(val, orientation() == Horizontal ? 0 : 1);
    plotter->end_plot();
}

void DispValue::plot2d(PlotAgent *plotter, int ndim) const
{
    if (type() == Array)
    {
      plotter->start_plot(make_title(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());
          }
      }
    }
    else
    {
      string prefix, suffix;
      get_index_surroundings(prefix, suffix);

      plotter->start_plot(prefix + "x" + suffix, ndim);

      for (int i = 0; i < nchildren(); i++)
      {
          DispValue *c = child(i);
          string idx = c->index(prefix, suffix);
          plotter->add_point(atof(idx.chars()), c->num_value());
      }
    }

    plotter->end_plot();
}

void DispValue::plot3d(PlotAgent *plotter, int ndim) const
{
    plotter->start_plot(make_title(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;



//-----------------------------------------------------------------------------
// Debugging
//-----------------------------------------------------------------------------

bool DispValue::OK() const
{
    assert (_links > 0);
    assert (_cached_box == 0 || _cached_box->OK());

    for (int i = 0; i < nchildren(); i++)
      assert(child(i)->OK());

    return true;
}

Generated by  Doxygen 1.6.0   Back to index