/* Implementation of the GDB variable objects API.
- Copyright (C) 1999-2017 Free Software Foundation, Inc.
+ Copyright (C) 1999-2020 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include "gdb_regex.h"
#include "varobj.h"
-#include "vec.h"
#include "gdbthread.h"
#include "inferior.h"
#include "varobj-iter.h"
+#include "parser-defs.h"
+#include "gdbarch.h"
#if HAVE_PYTHON
#include "python/python.h"
#include "python/python-internal.h"
-#include "python/py-ref.h"
#else
typedef int PyObject;
#endif
-/* Non-zero if we want to see trace of varobj level stuff. */
+/* See varobj.h. */
unsigned int varobjdebug = 0;
static void
else
fi = NULL;
- /* frame = -2 means always use selected frame. */
if (type == USE_SELECTED_FRAME)
var->root->floating = true;
}
p = expression;
- innermost_block = NULL;
+
+ innermost_block_tracker tracker (INNERMOST_BLOCK_FOR_SYMBOLS
+ | INNERMOST_BLOCK_FOR_REGISTERS);
/* Wrap the call to parse expression, so we can
return a sensible error. */
- TRY
+ try
{
- var->root->exp = parse_exp_1 (&p, pc, block, 0);
+ var->root->exp = parse_exp_1 (&p, pc, block, 0, &tracker);
}
- CATCH (except, RETURN_MASK_ERROR)
+ catch (const gdb_exception_error &except)
{
return NULL;
}
- END_CATCH
/* Don't allow variables to be created for types. */
if (var->root->exp->elts[0].opcode == OP_TYPE
}
var->format = variable_default_display (var.get ());
- var->root->valid_block = innermost_block;
+ var->root->valid_block =
+ var->root->floating ? NULL : tracker.block ();
var->name = expression;
/* For a root var, the name and the expr are the same. */
var->path_expr = expression;
we must select the appropriate frame before parsing
the expression, otherwise the value will not be current.
Since select_frame is so benign, just call it for all cases. */
- if (innermost_block)
+ if (var->root->valid_block)
{
/* User could specify explicit FRAME-ADDR which was not found but
EXPRESSION is frame specific and we would not be able to evaluate
error (_("Failed to find the specified frame"));
var->root->frame = get_frame_id (fi);
- var->root->thread_id = ptid_to_global_thread_id (inferior_ptid);
+ var->root->thread_id = inferior_thread ()->global_num;
old_id = get_frame_id (get_selected_frame (NULL));
select_frame (fi);
}
/* We definitely need to catch errors here.
If evaluate_expression succeeds we got the value we wanted.
But if it fails, we still go on with a call to evaluate_type(). */
- TRY
+ try
{
value = evaluate_expression (var->root->exp.get ());
}
- CATCH (except, RETURN_MASK_ERROR)
+ catch (const gdb_exception_error &except)
{
/* Error getting the value. Try to at least get the
right type. */
var->type = value_type (type_only_value);
}
- END_CATCH
if (value != NULL)
{
}
if (varobj_value_is_changeable_p (var)
- && var->value && !value_lazy (var->value))
+ && var->value != nullptr && !value_lazy (var->value.get ()))
{
- var->print_value = varobj_value_get_print_value (var->value,
+ var->print_value = varobj_value_get_print_value (var->value.get (),
var->format, var);
}
return var->frozen;
}
-/* A helper function that restricts a range to what is actually
- available in a VEC. This follows the usual rules for the meaning
- of FROM and TO -- if either is negative, the entire range is
- used. */
+/* A helper function that updates the contents of FROM and TO based on the
+ size of the vector CHILDREN. If the contents of either FROM or TO are
+ negative the entire range is used. */
void
varobj_restrict_range (const std::vector<varobj *> &children,
{
if (var->saved_item != NULL)
{
- value_free (var->saved_item->value);
+ value_decref (var->saved_item->value);
delete var->saved_item;
var->saved_item = NULL;
}
/* Release vitem->value so its lifetime is not bound to the
execution of a command. */
if (item != NULL && item->value != NULL)
- release_value_or_incref (item->value);
+ item->value = release_value (item->value).release ();
}
if (item == NULL)
while (!is_root_p (parent) && !is_path_expr_parent (parent))
parent = parent->parent;
+ /* Computation of full rooted expression for children of dynamic
+ varobjs is not supported. */
+ if (varobj_is_dynamic_p (parent))
+ error (_("Invalid variable object (child of a dynamic varobj)"));
+
return parent;
}
input_radix = 10; /* ALWAYS reset to decimal temporarily. */
expression_up exp = parse_exp_1 (&s, 0, 0, 0);
- TRY
+ try
{
value = evaluate_expression (exp.get ());
}
- CATCH (except, RETURN_MASK_ERROR)
+ catch (const gdb_exception_error &except)
{
/* We cannot proceed without a valid expression. */
return false;
}
- END_CATCH
/* All types that are editable must also be changeable. */
gdb_assert (varobj_value_is_changeable_p (var));
/* The value of a changeable variable object must not be lazy. */
- gdb_assert (!value_lazy (var->value));
+ gdb_assert (!value_lazy (var->value.get ()));
/* Need to coerce the input. We want to check if the
value of the variable object will be different
/* The new value may be lazy. value_assign, or
rather value_contents, will take care of this. */
- TRY
+ try
{
- val = value_assign (var->value, value);
+ val = value_assign (var->value.get (), value);
}
- CATCH (except, RETURN_MASK_ERROR)
+ catch (const gdb_exception_error &except)
{
return false;
}
- END_CATCH
/* If the value has changed, record it, so that next -var-update can
report this change. If a variable had a value of '1', we've set it
if (pretty_printing)
{
- PyObject *pretty_printer = NULL;
+ gdbpy_ref<> pretty_printer;
- if (var->value)
+ if (var->value != nullptr)
{
- pretty_printer = gdbpy_get_varobj_pretty_printer (var->value);
- if (! pretty_printer)
+ pretty_printer = gdbpy_get_varobj_pretty_printer (var->value.get ());
+ if (pretty_printer == nullptr)
{
gdbpy_print_stack ();
error (_("Cannot instantiate printer for default visualizer"));
}
}
-
+
if (pretty_printer == Py_None)
- {
- Py_DECREF (pretty_printer);
- pretty_printer = NULL;
- }
+ pretty_printer.reset (nullptr);
- install_visualizer (var->dynamic, NULL, pretty_printer);
+ install_visualizer (var->dynamic, NULL, pretty_printer.release ());
}
}
pretty_printer = NULL;
else
{
- pretty_printer = instantiate_pretty_printer (constructor, var->value);
+ pretty_printer = instantiate_pretty_printer (constructor,
+ var->value.get ());
if (! pretty_printer)
{
gdbpy_print_stack ();
if (value)
value = coerce_ref (value);
- if (var->type && TYPE_CODE (var->type) == TYPE_CODE_UNION)
+ if (var->type && var->type->code () == TYPE_CODE_UNION)
/* For unions, we need to fetch the value implicitly because
of implementation of union member fetch. When gdb
creates a value for a field and the value of the enclosing
{
/* For variables that are frozen, or are children of frozen
variables, we don't do fetch on initial assignment.
- For non-initial assignemnt we do the fetch, since it means we're
+ For non-initial assignment we do the fetch, since it means we're
explicitly asked to compare the new value with the old one. */
intentionally_not_fetched = true;
}
else
{
- TRY
+ try
{
value_fetch_lazy (value);
}
- CATCH (except, RETURN_MASK_ERROR)
+ catch (const gdb_exception_error &except)
{
/* Set the value to NULL, so that for the next -var-update,
we don't try to compare the new value with this value,
that we couldn't even read. */
value = NULL;
}
- END_CATCH
}
}
/* Get a reference now, before possibly passing it to any Python
code that might release it. */
+ value_ref_ptr value_holder;
if (value != NULL)
- value_incref (value);
+ value_holder = value_ref_ptr::new_reference (value);
/* Below, we'll be comparing string rendering of old and new
values. Don't get string rendering if the value is
{
/* Try to compare the values. That requires that both
values are non-lazy. */
- if (var->not_fetched && value_lazy (var->value))
+ if (var->not_fetched && value_lazy (var->value.get ()))
{
/* This is a frozen varobj and the value was never read.
Presumably, UI shows some "never read" indicator.
}
else
{
- gdb_assert (!value_lazy (var->value));
+ gdb_assert (!value_lazy (var->value.get ()));
gdb_assert (!value_lazy (value));
gdb_assert (!var->print_value.empty () && !print_value.empty ());
}
/* We must always keep the new value, since children depend on it. */
- if (var->value != NULL && var->value != value)
- value_free (var->value);
- var->value = value;
+ var->value = value_holder;
if (value && value_lazy (value) && intentionally_not_fetched)
var->not_fetched = true;
else
to see if the variable changed. */
if (var->dynamic->pretty_printer != NULL)
{
- print_value = varobj_value_get_print_value (var->value, var->format,
- var);
+ print_value = varobj_value_get_print_value (var->value.get (),
+ var->format, var);
if ((var->print_value.empty () && !print_value.empty ())
|| (!var->print_value.empty () && print_value.empty ())
|| (!var->print_value.empty () && !print_value.empty ()
}
var->print_value = print_value;
- gdb_assert (!var->value || value_type (var->value));
+ gdb_assert (var->value == nullptr || value_type (var->value.get ()));
return changed;
}
gdbpy_enter_varobj enter_py (var);
mainmod = PyImport_AddModule ("__main__");
- gdbpy_ref<> globals (PyModule_GetDict (mainmod));
- Py_INCREF (globals.get ());
-
+ gdbpy_ref<> globals
+ = gdbpy_ref<>::new_reference (PyModule_GetDict (mainmod));
gdbpy_ref<> constructor (PyRun_String (visualizer, Py_eval_input,
globals.get (), globals.get ()));
for which -var-list-children was never invoked. */
if (varobj_is_dynamic_p (v))
{
- std::vector<varobj *> changed, type_changed, unchanged, newobj;
+ std::vector<varobj *> changed, type_changed_vec, unchanged, newobj_vec;
bool children_changed = false;
if (v->frozen)
/* If update_dynamic_varobj_children returns false, then we have
a non-conforming pretty-printer, so we skip it. */
- if (update_dynamic_varobj_children (v, &changed, &type_changed, &newobj,
- &unchanged, &children_changed, true,
- v->from, v->to))
+ if (update_dynamic_varobj_children (v, &changed, &type_changed_vec,
+ &newobj_vec,
+ &unchanged, &children_changed,
+ true, v->from, v->to))
{
- if (children_changed || !newobj.empty ())
+ if (children_changed || !newobj_vec.empty ())
{
r.children_changed = true;
- r.newobj = std::move (newobj);
+ r.newobj = std::move (newobj_vec);
}
/* Push in reverse order so that the first child is
popped from the work stack first, and so will be
added to result first. This does not affect
correctness, just "nicer". */
- for (int i = type_changed.size () - 1; i >= 0; --i)
+ for (int i = type_changed_vec.size () - 1; i >= 0; --i)
{
- varobj_update_result r (type_changed[i]);
+ varobj_update_result item (type_changed_vec[i]);
/* Type may change only if value was changed. */
- r.changed = true;
- r.type_changed = true;
- r.value_installed = true;
+ item.changed = true;
+ item.type_changed = true;
+ item.value_installed = true;
- stack.push_back (std::move (r));
+ stack.push_back (std::move (item));
}
for (int i = changed.size () - 1; i >= 0; --i)
{
- varobj_update_result r (changed[i]);
+ varobj_update_result item (changed[i]);
- r.changed = true;
- r.value_installed = true;
+ item.changed = true;
+ item.value_installed = true;
- stack.push_back (std::move (r));
+ stack.push_back (std::move (item));
}
for (int i = unchanged.size () - 1; i >= 0; --i)
{
if (!unchanged[i]->frozen)
{
- varobj_update_result r (unchanged[i]);
+ varobj_update_result item (unchanged[i]);
- r.value_installed = true;
+ item.value_installed = true;
- stack.push_back (std::move (r));
+ stack.push_back (std::move (item));
}
}
if (r.changed || r.children_changed)
return true; /* OK */
}
-/* Unistall the object VAR. */
+/* Uninstall the object VAR. */
static void
uninstall_variable (struct varobj *var)
{
varobj_iter_delete (var->dynamic->child_iter);
varobj_clear_saved_item (var->dynamic);
- value_free (var->value);
if (is_root_p (var))
delete var->root;
value were accessible.
This differs from VAR->type in that VAR->type is always
- the true type of the expession in the source language.
+ the true type of the expression in the source language.
The return value of this function is the type we're
actually storing in varobj, and using for displaying
the values and for comparing previous and new values.
{
struct type *type;
- if (var->value)
- type = value_type (var->value);
+ if (var->value != nullptr)
+ type = value_type (var->value.get ());
else
type = var->type;
}
else
{
- ptid_t ptid = global_thread_id_to_ptid (var->root->thread_id);
+ thread_info *thread = find_thread_global_id (var->root->thread_id);
- if (!ptid_equal (minus_one_ptid, ptid))
+ if (thread != NULL)
{
- switch_to_thread (ptid);
+ switch_to_thread (thread);
within_scope = check_scope (var);
}
}
/* We need to catch errors here, because if evaluate
expression fails we want to just return NULL. */
- TRY
+ try
{
new_val = evaluate_expression (var->root->exp.get ());
}
- CATCH (except, RETURN_MASK_ERROR)
+ catch (const gdb_exception_error &except)
{
}
- END_CATCH
}
return new_val;
if (var->root->is_valid)
{
if (var->dynamic->pretty_printer != NULL)
- return varobj_value_get_print_value (var->value, var->format, var);
+ return varobj_value_get_print_value (var->value.get (), var->format,
+ var);
return (*var->root->lang_ops->value_of_variable) (var, format);
}
else
{
get_formatted_print_options (opts, format_code[(int) format]);
opts->deref_ref = 0;
- opts->raw = 1;
+ opts->raw = !pretty_printing;
}
std::string
{
struct value *replacement;
- gdbpy_ref<> output (apply_varobj_pretty_printer (value_formatter,
- &replacement,
- &stb));
+ gdbpy_ref<> output = apply_varobj_pretty_printer (value_formatter,
+ &replacement,
+ &stb);
/* If we have string like output ... */
if (output != NULL)
{
struct type *type;
- if (!(var->root->is_valid && var->value && VALUE_LVAL (var->value)))
+ if (!(var->root->is_valid && var->value != nullptr
+ && VALUE_LVAL (var->value.get ())))
return false;
type = varobj_get_value_type (var);
- switch (TYPE_CODE (type))
+ switch (type->code ())
{
case TYPE_CODE_STRUCT:
case TYPE_CODE_UNION:
type = varobj_get_value_type (var);
- switch (TYPE_CODE (type))
+ switch (type->code ())
{
case TYPE_CODE_STRUCT:
case TYPE_CODE_UNION:
all_root_varobjs (varobj_invalidate_iter, NULL);
}
+void _initialize_varobj ();
void
-_initialize_varobj (void)
+_initialize_varobj ()
{
varobj_table = XCNEWVEC (struct vlist *, VAROBJ_TABLE_SIZE);