This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Restored Objective-C language support


2016-09-13  Giah de Barag  <gdb@crelg.com>


Summary
-------

Patches are presented which restore objective-c language support.

These patches are relative to the head of the gdb-7.11-branch.


Impact
------

These changes impact only objective-c language mode.


Description
-----------

These patches enable GDB to (once again) evaluate objective-c
expressions:

(a) [object message:arg] messages,
(b) @"foo" NSStrings, and
(c) @selector(foo:bar:) selectors.
(d) "foo" c strings

These patches complete the previously-unfinished work of merging
objc-exp.y into c-exp.y.


Explanation of Changes
----------------------

Problem 1: Evaluating "foo" c-string literals fails in objective-c
           language mode.

    Description:

        (gdb) set language objective-c
        (gdb) p "hello"
        $1 = <error reading variable>

        Creating a basic C string literal does not work when
        language is objective-c.

    Solution:

        Change objective-c evaluate_subexp_standard to
        evaluate_subexp_c.

    Test:

        The current source language is "auto; currently objective-c".
        (gdb) p strlen("test")
        $1 = 4


Problem 2: Evaluating objc expressions fails in objective-c language
           mode. Examples:

           (a) [object message:arg] messages,
           (b) @"foo" NSStrings, and
           (c) @selector(foo:bar:) selectors.

    Description:

        (gdb) p [@"hello" performSelector:@selector(length)]

        Crashes and burns in many specactular ways, reported in
        bugs 20501 and 20503.

        /* file: c-exp.y */

        exp : NSSTRING
            { write_exp_elt_opcode (pstate, OP_OBJC_NSSTRING);
              write_exp_string (pstate, $1);
              write_exp_elt_opcode (pstate, OP_OBJC_NSSTRING); } ;

        exp : SELECTOR
            { write_exp_elt_opcode (pstate, OP_OBJC_SELECTOR);
              write_exp_string (pstate, $1);
              write_exp_elt_opcode (pstate, OP_OBJC_SELECTOR); } ;

        Communication between scanner and parser is broken; when the
        program attempts to reduce these productions, $1 is null.
        

    Solution:

        Establish communication between the scanner and the parser by
        creating a global variable, last_parsed_token, to hold the last
        scanned token (struct typed_stoken) for string and for selector.

        The existing @selector() scanner was weak. A new, robust
        @selector() scanner was written and inserted to improve quality.

        The string scanner puts a pointer to the last scanned token into
        last_parsed_token (in its choice of object names, gdb sometimes
        conflates the meaning of scanning and parsing, which we went along
        with). The newly-created selector scanner also does the same.

    Test:

        (gdb) p [@"hello" performSelector:@selector(length)]
        $2 = 5
        (gdb)

        Here is another test, more complicated and pedantic, that
        invokes all the features that were broken, creating an
        NSString on the fly and sending it a a message which turns it
        into an NSArray, then sending that NSArray a message which
        sends a message to all its objects and creates another NSArray
        out of the results.

        (gdb) po [[@"(london, paris, rome, berlin)" propertyList]  \
              performSelector:@selector(mappedArrayUsingSelector:) \
                   withObject:@selector(capitalizedString)]
        (London, Paris, Rome, Berlin)
        (gdb)

        This single invocation tests:
        ----------------------------
        * the “po” command
        * creating a literal NSString @"foo" (which responds to messages)
        * sending a message [obj message:arg] to the result of a message
        * referencing a selector @selector(foo) (two different ones in
          one invocation)


Bugs Fixed
----------

These patches fix all bugs reported in:

    https://sourceware.org/bugzilla/show_bug.cgi?id=20501
    https://sourceware.org/bugzilla/show_bug.cgi?id=20503

By the way, this bug is obsolete and should be updated:

    https://sourceware.org/bugzilla/show_bug.cgi?id=11925


Note to GDB Maintainers
-----------------------

Can you adapt these patches to all relevant branches, or do you need me to
provide patches relative to other branches?

I can supply a recipe to build GNUstep if you want to test this patch
yourself.

Also, this is the first time I am doing something like this, so if I am
neglecting any rule of communication of this list, please inform me, and I
will correct it.


Patches
-------

Here are the patches relative to gdb-7.11-branch. Do you need them
relative to the other branches?


----- CUT HERE -----
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index f99a7ab..d3a5a44 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -10563,6 +10563,7 @@ watchpoint_exp_is_const (const struct expression *exp)
 	case OP_TYPEID:
 	case OP_NAME:
 	case OP_OBJC_NSSTRING:
+	case OP_OBJC_SELECTOR:
 
 	case UNOP_NEG:
 	case UNOP_LOGICAL_NOT:
diff --git a/gdb/c-exp.y b/gdb/c-exp.y
index 9f2a229..4a6b0b0 100644
--- a/gdb/c-exp.y
+++ b/gdb/c-exp.y
@@ -129,6 +129,8 @@ void yyerror (char *);
 
 static int type_aggregate_p (struct type *);
 
+static struct typed_stoken *last_parsed_token;
+
 %}
 
 /* Although the yacc "value" of an expression is not used,
@@ -175,6 +177,8 @@ static struct stoken operator_stoken (const char *);
 static void check_parameter_typelist (VEC (type_ptr) *);
 static void write_destructor_name (struct parser_state *par_state,
 				   struct stoken);
+static void write_exp_nsstring (struct parser_state *par_state, struct typed_stoken *token);
+static void write_exp_selector (struct parser_state *par_state, struct typed_stoken *token);
 
 #ifdef YYBISON
 static void c_print_token (FILE *file, int type, YYSTYPE value);
@@ -209,7 +213,7 @@ static void c_print_token (FILE *file, int type, YYSTYPE value);
 
 %token <tsval> STRING
 %token <sval> NSSTRING		/* ObjC Foundation "NSString" literal */
-%token SELECTOR			/* ObjC "@selector" pseudo-operator   */
+%token <sval> SELECTOR		/* ObjC "@selector" pseudo-operator   */
 %token <tsval> CHAR
 %token <ssym> NAME /* BLOCKNAME defined below to give it higher precedence. */
 %token <ssym> UNKNOWN_CPP_NAME
@@ -779,10 +783,11 @@ exp	:	VARIABLE
 			}
 	;
 
-exp	:	SELECTOR '(' name ')'
-			{
-			  write_exp_elt_opcode (pstate, OP_OBJC_SELECTOR);
-			  write_exp_string (pstate, $3);
+exp	:	SELECTOR	/* ObjC selector literal
+				 * of the form @selector(foo:bar:)
+				 */
+			{ write_exp_elt_opcode (pstate, OP_OBJC_SELECTOR);
+			  write_exp_selector   (pstate, last_parsed_token);
 			  write_exp_elt_opcode (pstate, OP_OBJC_SELECTOR); }
 	;
 
@@ -894,11 +899,11 @@ exp	:	string_exp
 			}
 	;
 
-exp     :	NSSTRING	/* ObjC NextStep NSString constant
-				 * of the form '@' '"' string '"'.
+exp     :	NSSTRING	/* ObjC GNUstep NSString literal
+				 * of the form @"foo"
 				 */
 			{ write_exp_elt_opcode (pstate, OP_OBJC_NSSTRING);
-			  write_exp_string (pstate, $1);
+			  write_exp_nsstring   (pstate, last_parsed_token);
 			  write_exp_elt_opcode (pstate, OP_OBJC_NSSTRING); }
 	;
 
@@ -1659,6 +1664,26 @@ name_not_typename :	NAME
 
 %%
 
+static void
+write_exp_nsstring (struct parser_state *par_state, struct typed_stoken *value)
+/* Like write_exp_string, but for Objective C NSString literal, @"foo" */
+{
+  struct stoken token;
+  token.length = value->length;
+  token.ptr    = value->ptr;
+  write_exp_string (par_state, token);
+}
+
+static void
+write_exp_selector (struct parser_state *par_state, struct typed_stoken *value)
+/* Like write_exp_string, but for Objective C selector macro, @selector(foo:bar:) */
+{
+  struct stoken token;
+  token.length = value->length;
+  token.ptr    = value->ptr;
+  write_exp_string (par_state, token);
+}
+
 /* Like write_exp_string, but prepends a '~'.  */
 
 static void
@@ -2250,11 +2275,113 @@ parse_string_or_char (const char *tokptr, const char **outptr,
   value->ptr = (char *) obstack_base (&tempbuf);
   value->length = obstack_object_size (&tempbuf);
 
+  last_parsed_token = value;
+
   *outptr = tokptr;
 
   return quote == '"' ? (is_objc ? NSSTRING : STRING) : CHAR;
 }
 
+/* Scan an objc selector literal (described by the following regex) from
+   TOKEN_STREAM, place $1 (the value inside the literal parens) into VALUE
+   and return a pointer to the next input in the token_stream.
+ 
+       @?selector\(([0-9A-Za-z_:-]+)\)                                    
+ */
+static const char * 
+parse_objc_selector_literal (const char *tokptr, struct typed_stoken *value)
+{
+  const char	*s1  = NULL;
+  const char	*s2  = NULL;
+  const char	*str = NULL;	/* not null-terminated, just a pointer into token_stream */
+  int		 len = 0;
+  static unsigned sl;
+
+  /* skip any leading whitespace */
+  s1 = tokptr;
+  s1 = skip_spaces_const (s1);
+
+  /* parse the '@' */
+  if (*s1 == '@')
+    s1++;
+  s1 = skip_spaces_const (s1);
+
+  /* parse the word "selector" */
+  if (!sl)
+    sl = strlen("selector");
+  if (strncmp (s1, "selector", sl) != 0)
+    {
+      /* error (_("malformed @selector() macro: expected 'selector'")); */
+      return NULL;
+    }
+  s1 += sl;
+  s1 = skip_spaces_const (s1);
+
+  /* parse the open paren */
+  if (*s1 != '(')
+    {
+      error (_("malformed @selector() macro: expected '('"));
+      return NULL;
+    }
+  s1++;
+  s1 = skip_spaces_const (s1);
+  
+  str = s1;
+  s2  = s1;
+
+  /* parse the selector string [0-9A-Za-z_: -]) */
+  for (;;)
+    {
+      if (isalnum (*s2) || (*s2 == '_') || (*s2 == ':') || (*s2 == '-'))
+	;
+      else if (*s2 == ')' || isspace(*s2))
+	break;
+      else
+	{
+	  int buflen = s2 - s1;
+	  if (!tempbuf_init)
+	    tempbuf_init = 1;
+	  else
+	    obstack_free (&tempbuf, NULL);
+	  obstack_init (&tempbuf);
+	  obstack_grow (&tempbuf, str, buflen);
+	  obstack_1grow (&tempbuf, '\0');
+	  error ("illegal selector: \"%s\"", tempbuf);
+	  obstack_free (&tempbuf, NULL);
+	  return NULL;
+	}
+      s2++;
+    }
+  len = s2 - s1;
+
+  /* slurp up spaces if terminator was ')' */
+  s2++;
+  s2 = skip_spaces_const (s2);
+
+  /* slurp up ')' if terminator was space(s) */
+  if (*s2 == ')')
+      s2++;
+  s2 = skip_spaces_const (s2);
+
+  /* save the token for reference in the productions */
+  gdb_assert (value != NULL);
+  if (!tempbuf_init)
+      tempbuf_init = 1;
+  else
+      obstack_free (&tempbuf, NULL);
+
+  obstack_init (&tempbuf);
+  obstack_grow (&tempbuf, str, len);
+
+  value->type   = SELECTOR;
+  value->ptr    = (char *) obstack_base (&tempbuf);
+  value->length = obstack_object_size (&tempbuf);
+
+  last_parsed_token = value;
+
+  return s2;
+}
+
 /* This is used to associate some attributes with a token.  */
 
 enum token_flag
@@ -2655,14 +2782,14 @@ lex_one_token (struct parser_state *par_state, int *is_quoted_name)
 
 	if (parse_language (par_state)->la_language == language_objc)
 	  {
-	    size_t len = strlen ("selector");
-
-	    if (strncmp (p, "selector", len) == 0
-		&& (p[len] == '\0' || isspace (p[len])))
+	    /* try to parse a selector literal */
+	    const char *newlexptr = parse_objc_selector_literal(p, &yylval.tsval);
+	    if (newlexptr != NULL)
 	      {
-		lexptr = p + len;
+		lexptr = newlexptr; /* p + yylval.tsval.length */
 		return SELECTOR;
 	      }
+	    /* there was no selector so try to parse a string literal */
 	    else if (*p == '"')
 	      goto parse_string;
 	  }
@@ -3291,6 +3418,7 @@ c_print_token (FILE *file, int type, YYSTYPE value)
       break;
 
     case NSSTRING:
+    case SELECTOR:
     case VARIABLE:
       fprintf (file, "sval<%s>", copy_name (value.sval));
       break;
diff --git a/gdb/objc-lang.c b/gdb/objc-lang.c
index f58b1c4..d7ec15b 100644
--- a/gdb/objc-lang.c
+++ b/gdb/objc-lang.c
@@ -339,6 +339,7 @@ static const struct op_print objc_op_print_tab[] =
     {"/",  BINOP_DIV, PREC_MUL, 0},
     {"%",  BINOP_REM, PREC_MUL, 0},
     {"@",  BINOP_REPEAT, PREC_REPEAT, 0},
+    {"+",  UNOP_PLUS, PREC_PREFIX, 0},
     {"-",  UNOP_NEG, PREC_PREFIX, 0},
     {"!",  UNOP_LOGICAL_NOT, PREC_PREFIX, 0},
     {"~",  UNOP_COMPLEMENT, PREC_PREFIX, 0},
@@ -358,7 +359,7 @@ const struct language_defn objc_language_defn = {
   case_sensitive_on,
   array_row_major,
   macro_expansion_c,
-  &exp_descriptor_standard,
+  &exp_descriptor_c,
   c_parse,
   c_error,
   null_post_parser,
@@ -385,7 +386,7 @@ const struct language_defn objc_language_defn = {
   c_language_arch_info,
   default_print_array_index,
   default_pass_by_reference,
-  default_get_string,
+  c_get_string,
   NULL,				/* la_get_symbol_name_cmp */
   iterate_over_symbols,
   &default_varobj_ops,


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]