public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
From: Simon Marchi <simon.marchi@polymtl.ca>
To: "Maciej W. Rozycki" <macro@embecosm.com>, gdb-patches@sourceware.org
Cc: Andrew Burgess <aburgess@redhat.com>, Tom Tromey <tom@tromey.com>,
	Simon Sobisch <simonsobisch@web.de>
Subject: Re: [PATCH v8 4/6] GDB: Allow arbitrary keywords in integer set commands
Date: Thu, 12 Jan 2023 15:48:14 -0500	[thread overview]
Message-ID: <368aabac-7418-3323-841a-45a16f82b796@polymtl.ca> (raw)
In-Reply-To: <alpine.DEB.2.20.2211240305170.19931@tpp.orcam.me.uk>



On 11/24/22 06:22, Maciej W. Rozycki wrote:
> Rather than just `unlimited' allow the integer set commands (or command 
> options) to define arbitrary keywords for the user to use, removing 
> hardcoded arrangements for the `unlimited' keyword.
> 
> Remove the confusingly named `var_zinteger', `var_zuinteger' and 
> `var_zuinteger_unlimited' `set'/`show' command variable types redefining 
> them in terms of `var_uinteger', `var_integer' and `var_pinteger', which 
> have the range of [0;UINT_MAX], [INT_MIN;INT_MAX], and [0;INT_MAX] each.
> 
> Following existing practice `var_pinteger' allows extra negative values 
> to be used, however unlike `var_zuinteger_unlimited' any number of such 
> values can be defined rather than just `-1'.
> 
> The "p" in `var_pinteger' stands for "positive", for the lack of a more 
> appropriate unambiguous letter, even though 0 obviously is not positive; 
> "n" would be confusing as to whether it stands for "non-negative" or 
> "negative".
> 
> Add a new structure, `literal_def', the entries of which define extra 
> keywords allowed for a command and numerical values they correspond to.  
> Those values are not verified against the basic range supported by the 
> underlying variable type, allowing extra values to be allowed outside 
> that range, which may or may not be individually made visible to the 
> user.  An optional value translation is possible with the structure to 
> follow the existing practice for some commands where user-entered 0 is 
> internally translated to UINT_MAX or INT_MAX.  Such translation can now 
> be arbitrary.  Literals defined by this structure are automatically used 
> for completion as necessary.
> 
> So for example:
> 
> const literal_def integer_unlimited_literals[] =
>   {
>     { "unlimited", INT_MAX, 0 },
>     { nullptr }
>   };
> 
> defines an extra `unlimited' keyword and a user-visible 0 value, both of 
> which get translated to INT_MAX for the setting to be used with.
> 
> Similarly:
> 
> const literal_def zuinteger_unlimited_literals[] =
>   {
>     { "unlimited", -1, -1 },
>     { nullptr }
>   };
> 
> defines the same keyword and a corresponding user-visible -1 value that 
> is used for the requested setting.  If the last member were omitted (or 
> set to `{}') here, then only the keyword would be allowed for the user 
> to enter and while -1 would still be used internally trying to enter it 
> as a part of a command would result in an "integer -1 out of range" 
> error.
> 
> Use said error message in all cases (citing the invalid value requested) 
> replacing "only -1 is allowed to set as unlimited" previously used for 
> `var_zuinteger_unlimited' settings only rather than propagating it to 
> `var_pinteger' type.  It could only be used for the specific case where 
> a single extra `unlimited' keyword was defined standing for -1 and the 
> use of numeric equivalents is discouraged anyway as it is for historical 
> reasons only that they expose GDB internals, confusingly different 
> across variable types.  Similarly update the "must be >= -1" Guile error 
> message.
> 
> Redefine Guile and Python parameter types in terms of the new variable 
> types and interpret extra keywords as Scheme keywords and Python strings 
> used to communicate corresponding parameter values.  Do not add a new
> PARAM_INTEGER Guile parameter type, however do handle the `var_integer' 
> variable type now, permitting existing parameters defined by GDB proper, 
> such as `listsize', to be accessed from Scheme code.
> 
> With these changes in place it should be trivial for a Scheme or Python 
> programmer to expand the syntax of the `make-parameter' command and the 
> `gdb.Parameter' class initializer to have arbitrary extra literals along 
> with their internal representation supplied.
> 
> Update the testsuite accordingly.

This is a nice cleanup I think.  I didn't want to delay this further, so
I did the best review I could given the time I had.  I'm fine with you
merging this (you can add my Approved-By), we can always follow up with
more fixes.  If others would like to review it, it's always appreciated,
but given the time it's been sitting unreviewed, I kind of doubt that
will happen.

I noted a few minor things:

> ---
> Changes from v7:
> 
> - Handle the special case of returning -1 for the value of `unlimited' 
>   with `var_pinteger' parameters in Python code.
> 
> - Update Python documentation accordingly, including `unlimited' case 
>   descriptions from what used to be in 1/4.
> 
> - Pass extra literals outside erased args with `add_set_or_show_cmd' and 
>   up the call chain.
> 
> No change from v6.
> 
> Changes from v5:
> 
> - Add a translation layer from Guile and Python parameter types to new
>   GDB variable types and remove `var_zuinteger', `var_uinteger', and 
>   `var_zuinteger_unlimited' variable types altogether now.
> 
> - Add an optional `extra_literals' initialiser to the `setting' class
>   constructor.
> 
> - Remove the "only -1 is allowed to set as unlimited" error message
>   altogether now rather than propagating it to `var_pinteger' type.
> 
> - Make the `val' member of `struct literal_def' optional and remove the
>   `allow' member; simplify processing accordingly.
> 
> - Rename `zuinteger_unlimited_literals' to `pinteger_unlimited_literals',
>   making the names of all `*_unlimited_literals' arrays consistent with 
>   the corresponding `var_*' variable types.
> 
> - Rename `struct integer_option_def' to `struct pinteger_option_def',
>   observing it's come from `struct zuinteger_unlimited_option_def' and 
>   what used to be the `var_zuinteger_unlimited' now has `var_pinteger' 
>   semantics.
> 
> - Update the names of test flags used by `maint test-options' accordingly.
> 
> - Add constructor variants to `struct uinteger_option_def' and `struct
>   pinteger_option_def' that allow one to skip the `extra_literals' 
>   initialiser altogether rather than having to pass in `nullptr'.
> 
> - Update Python documentation mentioning the use of literal `unlimited'
>   with the respective parameter types.
> 
> No change from v4.
> 
> New change in v4.
> ---
>  gdb/cli/cli-cmds.c                        |   59 ++---
>  gdb/cli/cli-decode.c                      |  321 +++++++++++++++++++++++-------
>  gdb/cli/cli-option.c                      |  117 +++++++---
>  gdb/cli/cli-option.h                      |   54 +++--
>  gdb/cli/cli-setshow.c                     |  245 ++++++++++------------
>  gdb/cli/cli-setshow.h                     |   20 -
>  gdb/command.h                             |  110 +++++++---
>  gdb/doc/python.texi                       |   28 +-
>  gdb/guile/scm-param.c                     |  319 +++++++++++++++++++----------
>  gdb/maint-test-options.c                  |   44 ++--
>  gdb/python/py-param.c                     |  286 ++++++++++++++++----------
>  gdb/python/python.c                       |   52 +++-
>  gdb/testsuite/gdb.base/max-value-size.exp |    2 
>  gdb/testsuite/gdb.base/options.exp        |   47 ++--
>  gdb/testsuite/gdb.base/settings.exp       |    2 
>  gdb/testsuite/gdb.base/with.exp           |    2 
>  gdb/testsuite/gdb.guile/scm-parameter.exp |   23 --
>  gdb/testsuite/gdb.python/py-parameter.exp |   15 +
>  gdb/valprint.c                            |    9 
>  19 files changed, 1133 insertions(+), 622 deletions(-)
> 
> gdb-setshow-cmd-extra-literals.diff
> Index: src/gdb/cli/cli-cmds.c
> ===================================================================
> --- src.orig/gdb/cli/cli-cmds.c
> +++ src/gdb/cli/cli-cmds.c
> @@ -2213,22 +2213,40 @@ value_from_setting (const setting &var,
>  {
>    switch (var.type ())
>      {
> +    case var_uinteger:
>      case var_integer:
> -      if (var.get<int> () == INT_MAX)
> -	return value_from_longest (builtin_type (gdbarch)->builtin_int,
> -				   0);
> -      else
> -	return value_from_longest (builtin_type (gdbarch)->builtin_int,
> -				   var.get<int> ());
> -    case var_zinteger:
> -      return value_from_longest (builtin_type (gdbarch)->builtin_int,
> -				 var.get<int> ());
> +    case var_pinteger:
> +      {
> +	LONGEST value
> +	  = (var.type () == var_uinteger
> +	     ? static_cast<LONGEST> (var.get<unsigned int> ())
> +	     : static_cast<LONGEST> (var.get<int> ()));
> +
> +	if (var.extra_literals () != nullptr)
> +	  for (const literal_def *l = var.extra_literals ();
> +	       l->literal != nullptr;
> +	       l++)
> +	    if (value == l->use)
> +	      {
> +		if (l->val.has_value ())
> +		  value = *l->val;
> +		else
> +		  return allocate_value (builtin_type (gdbarch)->builtin_void);

I was wondering about this line returning void.  As far as I understand,
this isn't used today.  It would only be used if we defined a
literal_def without a user-visible value.  And the consequence would be
that for this setting, using $_gdb_setting("foo") would yield void for
that setting value.

> +		break;
> +	      }
> +
> +	if (var.type () == var_uinteger)
> +	  return
> +	    value_from_ulongest (builtin_type (gdbarch)->builtin_unsigned_int,
> +				 static_cast<const unsigned int> (value));
> +	else
> +	  return
> +	    value_from_longest (builtin_type (gdbarch)->builtin_int,
> +				static_cast<const int> (value));

Unnecessary consts in casts.

> Index: src/gdb/cli/cli-option.c
> ===================================================================
> --- src.orig/gdb/cli/cli-option.c
> +++ src/gdb/cli/cli-option.c
> @@ -38,7 +38,7 @@ union option_value
>    /* For var_uinteger options.  */
>    unsigned int uinteger;
>  
> -  /* For var_zuinteger_unlimited options.  */
> +  /* For var_integer and var_pinteger options.  */
>    int integer;
>  
>    /* For var_enum options.  */
> @@ -356,42 +356,55 @@ parse_option (gdb::array_view<const opti
>  	return option_def_and_value {*match, match_ctx, val};
>        }
>      case var_uinteger:
> -    case var_zuinteger_unlimited:
> +    case var_integer:
> +    case var_pinteger:
>        {
>  	if (completion != nullptr)
>  	  {
> -	    if (**args == '\0')
> -	      {
> -		/* Convenience to let the user know what the option
> -		   can accept.  Note there's no common prefix between
> -		   the strings on purpose, so that readline doesn't do
> -		   a partial match.  */
> -		completion->tracker.add_completion
> -		  (make_unique_xstrdup ("NUMBER"));
> -		completion->tracker.add_completion
> -		  (make_unique_xstrdup ("unlimited"));
> -		return {};
> -	      }
> -	    else if (startswith ("unlimited", *args))
> +	    if (match->extra_literals != nullptr)

The two ifs could be merged, it would help reduce the indentation a bit.

>  	      {
> -		completion->tracker.add_completion
> -		  (make_unique_xstrdup ("unlimited"));
> -		return {};
> +		/* Convenience to let the user know what the option can
> +		   accept.  Make sure there's no common prefix between
> +		   "NUMBER" and all the strings when adding new ones,
> +		   so that readline doesn't do a partial match.  */
> +		if (**args == '\0')
> +		  {
> +		    completion->tracker.add_completion
> +		      (make_unique_xstrdup ("NUMBER"));
> +		    for (const literal_def *l = match->extra_literals;
> +			 l->literal != nullptr;
> +			 l++)
> +		      completion->tracker.add_completion
> +			(make_unique_xstrdup (l->literal));
> +		    return {};
> +		  }
> +		else
> +		  {
> +		    bool completions = false;
> +		    for (const literal_def *l = match->extra_literals;
> +			 l->literal != nullptr;
> +			 l++)
> +		      if (startswith (l->literal, *args))
> +			{
> +			  completion->tracker.add_completion
> +			    (make_unique_xstrdup (l->literal));
> +			  completions = true;
> +			}
> +		    if (completions)
> +		      return {};
> +		  }
>  	      }
>  	  }
>  
> -	if (match->type == var_zuinteger_unlimited)
> -	  {
> -	    option_value val;
> -	    val.integer = parse_cli_var_zuinteger_unlimited (args, false);
> -	    return option_def_and_value {*match, match_ctx, val};
> -	  }
> +	LONGEST v = parse_cli_var_integer (match->type,
> +					   match->extra_literals,
> +					   args, false);
> +	option_value val;
> +	if (match->type == var_uinteger)
> +	  val.uinteger = v;
>  	else
> -	  {
> -	    option_value val;
> -	    val.uinteger = parse_cli_var_uinteger (match->type, args, false);
> -	    return option_def_and_value {*match, match_ctx, val};
> -	  }
> +	  val.integer = v;
> +	return option_def_and_value {*match, match_ctx, val};
>        }
>      case var_enum:
>        {
> @@ -593,7 +606,8 @@ save_option_value_in_ctx (gdb::optional<
>        *ov->option.var_address.uinteger (ov->option, ov->ctx)
>  	= ov->value->uinteger;
>        break;
> -    case var_zuinteger_unlimited:
> +    case var_integer:
> +    case var_pinteger:
>        *ov->option.var_address.integer (ov->option, ov->ctx)
>  	= ov->value->integer;
>        break;
> @@ -664,8 +678,20 @@ get_val_type_str (const option_def &opt,
>      case var_boolean:
>        return "[on|off]";
>      case var_uinteger:
> -    case var_zuinteger_unlimited:
> -      return "NUMBER|unlimited";
> +    case var_integer:
> +    case var_pinteger:
> +      {
> +	buffer = "NUMBER";
> +	if (opt.extra_literals != nullptr)
> +	  for (const literal_def *l = opt.extra_literals;
> +	       l->literal != nullptr;
> +	       l++)
> +	    {
> +	      buffer += '|';
> +	      buffer += l->literal;
> +	    }
> +	return buffer.c_str ();
> +      }
>      case var_enum:
>        {
>  	buffer = "";
> @@ -789,20 +815,31 @@ add_setshow_cmds_for_options (command_cl
>  	{
>  	  add_setshow_uinteger_cmd (option.name, cmd_class,
>  				    option.var_address.uinteger (option, data),
> +				    option.extra_literals,
>  				    option.set_doc, option.show_doc,
>  				    option.help_doc,
>  				    nullptr, option.show_cmd_cb,
>  				    set_list, show_list);
>  	}
> -      else if (option.type == var_zuinteger_unlimited)
> +      else if (option.type == var_integer)
>  	{
> -	  add_setshow_zuinteger_unlimited_cmd
> -	    (option.name, cmd_class,
> -	     option.var_address.integer (option, data),
> -	     option.set_doc, option.show_doc,
> -	     option.help_doc,
> -	     nullptr, option.show_cmd_cb,
> -	     set_list, show_list);
> +	  add_setshow_integer_cmd (option.name, cmd_class,
> +				   option.var_address.integer (option, data),
> +				   option.extra_literals,
> +				   option.set_doc, option.show_doc,
> +				   option.help_doc,
> +				   nullptr, option.show_cmd_cb,
> +				   set_list, show_list);
> +	}
> +      else if (option.type == var_pinteger)
> +	{
> +	  add_setshow_pinteger_cmd (option.name, cmd_class,
> +				    option.var_address.integer (option, data),
> +				    option.extra_literals,
> +				    option.set_doc, option.show_doc,
> +				    option.help_doc,
> +				    nullptr, option.show_cmd_cb,
> +				    set_list, show_list);
>  	}
>        else if (option.type == var_enum)
>  	{
> Index: src/gdb/cli/cli-option.h
> ===================================================================
> --- src.orig/gdb/cli/cli-option.h
> +++ src/gdb/cli/cli-option.h
> @@ -49,12 +49,13 @@ struct option_def
>       used to create the option's "set/show" commands.  */
>    constexpr option_def (const char *name_,
>  			var_types var_type_,
> +			const literal_def *extra_literals_,
>  			erased_get_var_address_ftype *erased_get_var_address_,
>  			show_value_ftype *show_cmd_cb_,
>  			const char *set_doc_,
>  			const char *show_doc_,
>  			const char *help_doc_)
> -    : name (name_), type (var_type_),
> +    : name (name_), type (var_type_), extra_literals (extra_literals_),
>        erased_get_var_address (erased_get_var_address_),
>        var_address {},
>        show_cmd_cb (show_cmd_cb_),
> @@ -68,6 +69,9 @@ struct option_def
>    /* The option's type.  */
>    var_types type;
>  
> +  /* Extra literals, such as `unlimited', accepted in lieu of a number.  */
> +  const literal_def *extra_literals;
> +
>    /* A function that gets the controlling variable's address, type
>       erased.  */
>    erased_get_var_address_ftype *erased_get_var_address;
> @@ -160,7 +164,7 @@ struct boolean_option_def : option_def
>  		      const char *set_doc_,
>  		      const char *show_doc_ = nullptr,
>  		      const char *help_doc_ = nullptr)
> -    : option_def (long_option_, var_boolean,
> +    : option_def (long_option_, var_boolean, nullptr,
>  		  (erased_get_var_address_ftype *) get_var_address_cb_,
>  		  show_cmd_cb_,
>  		  set_doc_, show_doc_, help_doc_)
> @@ -207,37 +211,59 @@ struct uinteger_option_def : option_def
>  {
>    uinteger_option_def (const char *long_option_,
>  		       unsigned int *(*get_var_address_cb_) (Context *),
> +		       const literal_def *extra_literals_,
>  		       show_value_ftype *show_cmd_cb_,
>  		       const char *set_doc_,
>  		       const char *show_doc_ = nullptr,
>  		       const char *help_doc_ = nullptr)
> -    : option_def (long_option_, var_uinteger,
> +    : option_def (long_option_, var_uinteger, extra_literals_,
>  		  (erased_get_var_address_ftype *) get_var_address_cb_,
>  		  show_cmd_cb_,
>  		  set_doc_, show_doc_, help_doc_)
>    {
>      var_address.uinteger = detail::get_var_address<unsigned int, Context>;
>    }
> +
> +  uinteger_option_def (const char *long_option_,
> +		       unsigned int *(*get_var_address_cb_) (Context *),
> +		       show_value_ftype *show_cmd_cb_,
> +		       const char *set_doc_,
> +		       const char *show_doc_ = nullptr,
> +		       const char *help_doc_ = nullptr)
> +    : uinteger_option_def (long_option_, get_var_address_cb_, nullptr,
> +			   show_cmd_cb_, set_doc_, show_doc_, help_doc_)
> +  { /* Nothing.  */ }
>  };
>  
> -/* A var_zuinteger_unlimited command line option.  */
> +/* A var_pinteger command line option.  */
>  
>  template<typename Context>
> -struct zuinteger_unlimited_option_def : option_def
> +struct pinteger_option_def : option_def
>  {
> -  zuinteger_unlimited_option_def (const char *long_option_,
> -				  int *(*get_var_address_cb_) (Context *),
> -				  show_value_ftype *show_cmd_cb_,
> -				  const char *set_doc_,
> -				  const char *show_doc_ = nullptr,
> -				  const char *help_doc_ = nullptr)
> -    : option_def (long_option_, var_zuinteger_unlimited,
> +  pinteger_option_def (const char *long_option_,
> +		       int *(*get_var_address_cb_) (Context *),
> +		       const literal_def *extra_literals_,
> +		       show_value_ftype *show_cmd_cb_,
> +		       const char *set_doc_,
> +		       const char *show_doc_ = nullptr,
> +		       const char *help_doc_ = nullptr)
> +    : option_def (long_option_, var_pinteger, extra_literals_,
>  		  (erased_get_var_address_ftype *) get_var_address_cb_,
>  		  show_cmd_cb_,
>  		  set_doc_, show_doc_, help_doc_)
>    {
>      var_address.integer = detail::get_var_address<int, Context>;
>    }
> +
> +  pinteger_option_def (const char *long_option_,
> +		       int *(*get_var_address_cb_) (Context *),
> +		       show_value_ftype *show_cmd_cb_,
> +		       const char *set_doc_,
> +		       const char *show_doc_ = nullptr,
> +		       const char *help_doc_ = nullptr)
> +    : pinteger_option_def (long_option_, get_var_address_cb_, nullptr,
> +			   show_cmd_cb_, set_doc_, show_doc_, help_doc_)
> +  { /* Nothing.  */ }
>  };
>  
>  /* An var_enum command line option.  */
> @@ -252,7 +278,7 @@ struct enum_option_def : option_def
>  		   const char *set_doc_,
>  		   const char *show_doc_ = nullptr,
>  		   const char *help_doc_ = nullptr)
> -    : option_def (long_option_, var_enum,
> +    : option_def (long_option_, var_enum, nullptr,
>  		  (erased_get_var_address_ftype *) get_var_address_cb_,
>  		  show_cmd_cb_,
>  		  set_doc_, show_doc_, help_doc_)
> @@ -273,7 +299,7 @@ struct string_option_def : option_def
>  		     const char *set_doc_,
>  		     const char *show_doc_ = nullptr,
>  		     const char *help_doc_ = nullptr)
> -    : option_def (long_option_, var_string,
> +    : option_def (long_option_, var_string, nullptr,
>  		  (erased_get_var_address_ftype *) get_var_address_cb_,
>  		  show_cmd_cb_,
>  		  set_doc_, show_doc_, help_doc_)
> Index: src/gdb/cli/cli-setshow.c
> ===================================================================
> --- src.orig/gdb/cli/cli-setshow.c
> +++ src/gdb/cli/cli-setshow.c
> @@ -149,10 +149,11 @@ deprecated_show_value_hack (struct ui_fi
>      }
>  }
>  
> -/* Returns true if ARG is "unlimited".  */
> +/* Returns true and the value in VAL if ARG is an accepted literal.  */
>  
>  static bool
> -is_unlimited_literal (const char **arg, bool expression)
> +get_literal_val (LONGEST &val, const literal_def *extra_literals,
> +		 const char **arg, bool expression)
>  {
>    *arg = skip_spaces (*arg);
>  
> @@ -162,85 +163,104 @@ is_unlimited_literal (const char **arg,
>  
>    size_t len = p - *arg;
>  
> -  if (len > 0 && strncmp ("unlimited", *arg, len) == 0)
> -    {
> -      *arg += len;
> -
> -      /* If parsing an expression (i.e., parsing for a "set" command),
> -	 anything after "unlimited" is junk.  For options, anything
> -	 after "unlimited" might be a command argument or another
> -	 option.  */
> -      if (expression)
> +  if (len > 0 && extra_literals != nullptr)
> +    for (const literal_def *l = extra_literals;
> +	 l->literal != nullptr;
> +	 l++)
> +      if (strncmp (l->literal, *arg, len) == 0)
>  	{
> -	  const char *after = skip_spaces (*arg);
> -	  if (*after != '\0')
> -	    error (_("Junk after \"%.*s\": %s"),
> -		   (int) len, unl_start, after);
> -	}
> +	  *arg += len;
>  
> -      return true;
> -    }
> +	  /* If parsing an expression (i.e., parsing for a "set" command),
> +	     anything after the literal is junk.  For options, anything
> +	     after the literal might be a command argument or another
> +	     option.  */
> +	  if (expression)
> +	    {
> +	      const char *after = skip_spaces (*arg);
> +	      if (*after != '\0')
> +		error (_("Junk after \"%.*s\": %s"),
> +		       (int) len, unl_start, after);
> +	    }
> +
> +	  val = l->use;
> +	  return true;
> +	}
>  
>    return false;
>  }
>  
>  /* See cli-setshow.h.  */
>  
> -unsigned int
> -parse_cli_var_uinteger (var_types var_type, const char **arg,
> -			bool expression)
> +LONGEST
> +parse_cli_var_integer (var_types var_type, const literal_def *extra_literals,
> +		       const char **arg, bool expression)

The function here could perhaps  take a `const setting &`, instead of var_type
and extra_literals, which are properties of a setting.

> @@ -623,36 +614,32 @@ get_setshow_command_value_string (const
>  	}
>        break;
>      case var_uinteger:
> -    case var_zuinteger:
> -      {
> -	const unsigned int value = var.get<unsigned int> ();
> -
> -	if (var.type () == var_uinteger
> -	    && value == UINT_MAX)
> -	  stb.puts ("unlimited");
> -	else
> -	  stb.printf ("%u", value);
> -      }
> -      break;
>      case var_integer:
> -    case var_zinteger:
> +    case var_pinteger:
>        {
> -	const int value = var.get<int> ();
> +	bool printed = false;
> +	const LONGEST value
> +	  = (var.type () == var_uinteger
> +	     ? static_cast<LONGEST> (var.get<unsigned int> ())
> +	     : static_cast<LONGEST> (var.get<int> ()));
>  
> -	if (var.type () == var_integer
> -	    && value == INT_MAX)
> -	  stb.puts ("unlimited");
> -	else
> -	  stb.printf ("%d", value);
> -      }
> -      break;
> -    case var_zuinteger_unlimited:
> -      {
> -	const int value = var.get<int> ();
> -	if (value == -1)
> -	  stb.puts ("unlimited");
> -	else
> -	  stb.printf ("%d", value);
> +	if (var.extra_literals () != nullptr)
> +	  for (const literal_def *l = var.extra_literals ();
> +	       l->literal != nullptr;
> +	       l++)
> +	    if (value == l->use)
> +	      {
> +		stb.puts (l->literal);
> +		printed = true;
> +		break;
> +	      }
> +	if (!printed)
> +	  {
> +	    if (var.type () == var_uinteger)
> +	      stb.printf ("%u", static_cast<const unsigned int> (value));
> +	    else
> +	      stb.printf ("%d", static_cast<const int> (value));

The consts are unnecessary here.

> @@ -106,22 +108,25 @@ enum var_types
>      var_optional_filename,
>      /* String which stores a filename.  (*VAR) is a std::string.  */
>      var_filename,
> -    /* ZeroableInteger.  *VAR is an int.  Like var_integer except
> -       that zero really means zero.  */
> -    var_zinteger,
> -    /* ZeroableUnsignedInteger.  *VAR is an unsigned int.  Zero really
> -       means zero.  */
> -    var_zuinteger,
> -    /* ZeroableUnsignedInteger with unlimited value.  *VAR is an int,
> -       but its range is [0, INT_MAX].  -1 stands for unlimited and
> -       other negative numbers are not allowed.  */
> -    var_zuinteger_unlimited,
>      /* Enumerated type.  Can only have one of the specified values.
>         *VAR is a char pointer to the name of the element that we
>         find.  */
>      var_enum
>    };
>  
> +/* A structure describing an extra literal accepted and shown in place
> +   of a number.  */
> +struct literal_def
> +  {
> +    /* The literal to define, e.g. "unlimited".  */
> +    const char *literal;
> +    /* The number to substitute internally for LITERAL or VAL;
> +       the use of this number is not allowed (unless the same as VAL).  */
> +    LONGEST use;
> +    /* An optional number accepted that stands for the literal.  */
> +    gdb::optional<LONGEST> val;

Could you please add an empty line between each field?  I find that
makes it easier to read.

> +  };

The body of the struct should have one less indentation level (curly
braces at column 0).

> @@ -218,8 +222,8 @@ struct setting
>  
>       Type T must match the var type VAR_TYPE (see VAR_TYPE_USES).  */
>    template<typename T>
> -  setting (var_types var_type, T *var)
> -    : m_var_type (var_type), m_var (var)
> +  setting (var_types var_type, T *var, const void *extra_literals = nullptr)
> +    : m_var_type (var_type), m_var (var), m_extra_literals (extra_literals)

Any reason why extra_literals and m_extra_literals are void pointers,
rather than literal_def pointers?

> @@ -110,6 +116,50 @@ struct param_smob
>    SCM containing_scm;
>  };
>  
> +/* Guile parameter types as in PARAMETER_TYPES later on.  */
> +
> +typedef enum param_types
> +  {
> +    param_boolean,
> +    param_auto_boolean,
> +    param_zinteger,
> +    param_uinteger,
> +    param_zuinteger,
> +    param_zuinteger_unlimited,
> +    param_string,
> +    param_string_noescape,
> +    param_optional_filename,
> +    param_filename,
> +    param_enum,
> +  }
> +param_types;

Just "enum param_types, no need for the typedef.  Braces at column 0.

> +
> +/* Translation from Guile parameters to GDB variable types.  Keep in the
> +   same order as PARAM_TYPES due to C++'s lack of designated initializers.  */
> +
> +static const struct
> +  {
> +    /* The type of the parameter.  */
> +    enum var_types type;
> +
> +    /* Extra literals, such as `unlimited', accepted in lieu of a number.  */
> +    const literal_def *extra_literals;
> +  }

Braces at column 0 above.

> @@ -631,20 +672,29 @@ pascm_param_value (const setting &var, i
>  	  return auto_keyword;
>        }
>  
> -    case var_zuinteger_unlimited:
> -      if (var.get<int> () == -1)
> -	return unlimited_keyword;
> -      gdb_assert (var.get<int> () >= 0);
> -      /* Fall through.  */
> -    case var_zinteger:
> -      return scm_from_int (var.get<int> ());
> -
>      case var_uinteger:
> -      if (var.get<unsigned int> ()== UINT_MAX)
> -	return unlimited_keyword;
> -      /* Fall through.  */
> -    case var_zuinteger:
> -      return scm_from_uint (var.get<unsigned int> ());
> +    case var_integer:
> +    case var_pinteger:
> +      {
> +	LONGEST value
> +	  = (var.type () == var_uinteger
> +	     ? static_cast<LONGEST> (var.get<unsigned int> ())
> +	     : static_cast<LONGEST> (var.get<int> ()));
> +
> +	if (var.extra_literals () != nullptr)
> +	  for (const literal_def *l = var.extra_literals ();
> +	       l->literal != nullptr;
> +	       l++)
> +	    if (value == l->use)
> +	      return scm_from_latin1_keyword (l->literal);
> +	if (var.type () == var_pinteger)
> +	  gdb_assert (value >= 0);
> +
> +	if (var.type () == var_uinteger)
> +	  return scm_from_uint (static_cast<const unsigned int> (value));
> +	else
> +	  return scm_from_int (static_cast<const int> (value));

Unnecessary consts in casts.

> +      }
>  
>      default:
>        break;
> @@ -735,53 +785,91 @@ pascm_set_param_value_x (param_smob *p_s
>  	var.set<enum auto_boolean> (AUTO_BOOLEAN_FALSE);
>        break;
>  
> -    case var_zinteger:
> +    case var_integer:
>      case var_uinteger:
> -    case var_zuinteger:
> -    case var_zuinteger_unlimited:
> -      if (var.type () == var_uinteger
> -	  || var.type () == var_zuinteger_unlimited)
> -	{
> -	  SCM_ASSERT_TYPE (scm_is_integer (value)
> -			   || scm_is_eq (value, unlimited_keyword),
> -			   value, arg_pos, func_name,
> -			   _("integer or #:unlimited"));
> -	  if (scm_is_eq (value, unlimited_keyword))
> +    case var_pinteger:
> +      {
> +	const literal_def *extra_literals = p_smob->extra_literals;
> +	enum tribool allowed = TRIBOOL_UNKNOWN;
> +	enum var_types var_type = var.type ();
> +	bool integer = scm_is_integer (value);
> +	bool keyword = scm_is_keyword (value);
> +	std::string buffer = "";
> +	size_t count = 0;
> +	LONGEST val;
> +
> +	if (extra_literals != nullptr)
> +	  for (const literal_def *l = extra_literals;
> +	       l->literal != nullptr;
> +	       l++, count++)
>  	    {
> -	      if (var.type () == var_uinteger)
> -		var.set<unsigned int> (UINT_MAX);
> -	      else
> -		var.set<int> (-1);
> -	      break;
> +	      if (count != 0)
> +		buffer += ", ";
> +	      buffer = buffer + "#:" + l->literal;
> +	      if (keyword
> +		  && allowed == TRIBOOL_UNKNOWN
> +		  && scm_is_eq (value,
> +				scm_from_latin1_keyword (l->literal)))
> +		{
> +		  val = l->use;
> +		  allowed = TRIBOOL_TRUE;
> +		}
>  	    }
> -	}
> -      else
> -	{
> -	  SCM_ASSERT_TYPE (scm_is_integer (value), value, arg_pos, func_name,
> -			   _("integer"));
> -	}
>  
> -      if (var.type () == var_uinteger
> -	  || var.type () == var_zuinteger)
> -	{
> -	  unsigned int u = scm_to_uint (value);
> +	if (allowed == TRIBOOL_UNKNOWN)
> +	  {
> +	    if (extra_literals == nullptr)
> +	      SCM_ASSERT_TYPE (integer, value, arg_pos, func_name,
> +			       _("integer"));
> +	    else if (count > 1)
> +	      SCM_ASSERT_TYPE (integer, value, arg_pos, func_name,
> +			       string_printf (_("integer or one of: %s"),
> +					      buffer.c_str ()).c_str ());
> +	    else
> +	      SCM_ASSERT_TYPE (integer, value, arg_pos, func_name,
> +			       string_printf (_("integer or %s"),
> +					      buffer.c_str ()).c_str ());
>  
> -	  if (var.type () == var_uinteger && u == 0)
> -	    u = UINT_MAX;
> -	  var.set<unsigned int> (u);
> -	}
> -      else
> -	{
> -	  int i = scm_to_int (value);
> +	    val = (var_type == var_uinteger
> +		   ? static_cast<LONGEST> (scm_to_uint (value))
> +		   : static_cast<LONGEST> (scm_to_int (value)));
>  
> -	  if (var.type () == var_zuinteger_unlimited && i < -1)
> -	    {
> -	      gdbscm_out_of_range_error (func_name, arg_pos, value,
> -					 _("must be >= -1"));
> +	    if (extra_literals != nullptr)
> +	      for (const literal_def *l = extra_literals;
> +		   l->literal != nullptr;
> +		   l++)
> +		{
> +		  if (l->val.has_value () && val == *l->val)
> +		    {
> +		      allowed = TRIBOOL_TRUE;
> +		      val = l->use;
> +		      break;
> +		    }
> +		  else if (val == l->use)
> +		    allowed = TRIBOOL_FALSE;
> +		}
>  	    }
> -	  var.set<int> (i);
> -	}
> -      break;
> +
> +	if (allowed == TRIBOOL_UNKNOWN)
> +	  {
> +	    if (val > UINT_MAX || val < INT_MIN
> +		|| (var_type == var_uinteger && val < 0)
> +		|| (var_type == var_integer && val > INT_MAX)
> +		|| (var_type == var_pinteger && val < 0)
> +		|| (var_type == var_pinteger && val > INT_MAX))
> +	      allowed = TRIBOOL_FALSE;
> +	  }
> +	if (allowed == TRIBOOL_FALSE)
> +	  gdbscm_out_of_range_error (func_name, arg_pos, value,
> +				     _("integer out of range"));
> +
> +	if (var_type == var_uinteger)
> +	  var.set<unsigned int> (static_cast<const unsigned int> (val));
> +	else
> +	  var.set<int> (static_cast<const int> (val));

Unnecessary consts in casts.

> Index: src/gdb/python/py-param.c
> ===================================================================
> --- src.orig/gdb/python/py-param.c
> +++ src/gdb/python/py-param.c
> @@ -28,24 +28,70 @@
>  #include "language.h"
>  #include "arch-utils.h"
>  
> +/* Python parameter types as in PARM_CONSTANTS below.  */
> +
> +typedef enum param_types
> +  {
> +    param_boolean,
> +    param_auto_boolean,
> +    param_uinteger,
> +    param_integer,
> +    param_string,
> +    param_string_noescape,
> +    param_optional_filename,
> +    param_filename,
> +    param_zinteger,
> +    param_zuinteger,
> +    param_zuinteger_unlimited,
> +    param_enum,
> +  }
> +param_types;

Just "enum param_types, no need for the typedef.  Braces at column 0.

I think that having two `param_types` enums (here and in the Guile
support) technically violates the one definition rule.  Not sure if that
matters in practice.

> +
> +/* Translation from Python parameters to GDB variable types.  Keep in the
> +   same order as PARAM_TYPES due to C++'s lack of designated initializers.  */
> +
> +static const struct
> +  {
> +    /* The type of the parameter.  */
> +    enum var_types type;
> +
> +    /* Extra literals, such as `unlimited', accepted in lieu of a number.  */
> +    const literal_def *extra_literals;
> +  }

Braces at column 0.

> Index: src/gdb/python/python.c
> ===================================================================
> --- src.orig/gdb/python/python.c
> +++ src/gdb/python/python.c
> @@ -504,27 +504,45 @@ gdbpy_parameter_value (const setting &va
>  	  Py_RETURN_NONE;
>        }
>  
> -    case var_integer:
> -      if (var.get<int> () == INT_MAX)
> -	Py_RETURN_NONE;
> -      /* Fall through.  */
> -    case var_zinteger:
> -    case var_zuinteger_unlimited:
> -      return gdb_py_object_from_longest (var.get<int> ()).release ();
> -
>      case var_uinteger:
> +    case var_integer:
> +    case var_pinteger:
>        {
> -	unsigned int val = var.get<unsigned int> ();
> +	LONGEST value
> +	  = (var.type () == var_uinteger
> +	     ? static_cast<LONGEST> (var.get<unsigned int> ())
> +	     : static_cast<LONGEST> (var.get<int> ()));
>  
> -	if (val == UINT_MAX)
> -	  Py_RETURN_NONE;
> -	return gdb_py_object_from_ulongest (val).release ();
> -      }
> +	if (var.extra_literals () != nullptr)
> +	  for (const literal_def *l = var.extra_literals ();
> +	       l->literal != nullptr;
> +	       l++)
> +	    if (value == l->use)
> +	      {
> +		if (strcmp (l->literal, "unlimited") == 0)
> +		  {
> +		    /* Compatibility hack for API brokennes.  */

brokenness

> +		    if (var.type () == var_pinteger
> +			&& l->val.has_value ()
> +			&& *l->val == -1)
> +		      value = -1;
> +		    else
> +		      Py_RETURN_NONE;
> +		  }
> +		else if (l->val.has_value ())
> +		  value = *l->val;
> +		else
> +		  return host_string_to_python_string (l->literal).release ();
> +	      }
>  
> -    case var_zuinteger:
> -      {
> -	unsigned int val = var.get<unsigned int> ();
> -	return gdb_py_object_from_ulongest (val).release ();
> +	if (var.type () == var_uinteger)
> +	  return
> +	    gdb_py_object_from_ulongest
> +	      (static_cast<const unsigned int> (value)).release ();
> +	else
> +	  return
> +	    gdb_py_object_from_longest
> +	      (static_cast<const int> (value)).release ();

Unnecessary const in casts.

Simon

  parent reply	other threads:[~2023-01-12 20:48 UTC|newest]

Thread overview: 32+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-11-24 11:21 [PATCH v8 0/6] gdb: split array and string limiting options Maciej W. Rozycki
2022-11-24 11:21 ` [PATCH v8 1/6] GDB: Fix documentation for `theclass' parameters in cli-decode.c Maciej W. Rozycki
2023-01-12 18:36   ` Simon Marchi
2023-01-18 21:56     ` Maciej W. Rozycki
2022-11-24 11:22 ` [PATCH v8 2/6] GDB: Add missing inline documentation for `add_setshow_cmd_full' Maciej W. Rozycki
2023-01-12 18:40   ` Simon Marchi
2023-01-18 23:24     ` [COMMITTED PATCH v9 2.0/6] " Maciej W. Rozycki
2023-01-18 23:24       ` [COMMITTED PATCH v9 2.1/6] GDB: Correct inline documentation for `add_setshow_cmd_full_erased' Maciej W. Rozycki
2023-01-18 23:24       ` [COMMITTED PATCH v9 2.2/6] GDB: Add missing inline documentation for `add_setshow_cmd_full' Maciej W. Rozycki
2022-11-24 11:22 ` [PATCH v8 3/6] GDB: Add references to erased args in cli-decode.c Maciej W. Rozycki
2023-01-12 18:46   ` Simon Marchi
2023-01-18 23:41     ` Maciej W. Rozycki
2022-11-24 11:22 ` [PATCH v8 4/6] GDB: Allow arbitrary keywords in integer set commands Maciej W. Rozycki
2022-11-24 11:57   ` Eli Zaretskii
2023-01-12 20:48   ` Simon Marchi [this message]
2023-01-19 21:17     ` Maciej W. Rozycki
2023-01-19 21:18       ` [COMMITTED PATCH v9 " Maciej W. Rozycki
2023-01-20 17:16       ` [PATCH v8 " Simon Marchi
2022-11-24 11:22 ` [PATCH v8 5/6] GDB: Add a character string limiting option Maciej W. Rozycki
2023-01-16 19:35   ` Simon Marchi
2023-01-19 21:17     ` Maciej W. Rozycki
2023-01-19 21:19       ` [COMMITTED PATCH v9 " Maciej W. Rozycki
2023-01-21  9:57         ` Simon Sobisch
2023-01-21 18:45           ` Simon Marchi
2023-01-21 19:29             ` Simon Sobisch
2023-02-07  5:17               ` Joel Brobecker
2022-11-24 11:23 ` [PATCH v8 6/6] GDB/testsuite: Expand for character string limiting options Maciej W. Rozycki
2023-01-16 21:07   ` Simon Marchi
2023-01-19 21:18     ` Maciej W. Rozycki
2023-01-19 21:20       ` [COMMITTED PATCH v9 " Maciej W. Rozycki
2022-12-08 12:05 ` [PING][PATCH v8 0/6] gdb: split array and " Maciej W. Rozycki
2023-01-09 12:27 ` [PING^2][PATCH " Maciej W. Rozycki

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=368aabac-7418-3323-841a-45a16f82b796@polymtl.ca \
    --to=simon.marchi@polymtl.ca \
    --cc=aburgess@redhat.com \
    --cc=gdb-patches@sourceware.org \
    --cc=macro@embecosm.com \
    --cc=simonsobisch@web.de \
    --cc=tom@tromey.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).