From patchwork Tue Sep 13 21:20:57 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Giah de Barag X-Patchwork-Id: 15605 Received: (qmail 58317 invoked by alias); 13 Sep 2016 21:21:07 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 58295 invoked by uid 89); 13 Sep 2016 21:21:07 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=0.6 required=5.0 tests=AWL, BAYES_40, RCVD_IN_DNSWL_NONE, SPF_SOFTFAIL autolearn=no version=3.3.2 spammy=sl, rome, Rome, nextstep X-HELO: telf.telf.com Received: from tel.telf.com (HELO telf.telf.com) (192.254.205.15) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Tue, 13 Sep 2016 21:20:59 +0000 Received: from rrcs-70-60-125-182.midsouth.biz.rr.com ([70.60.125.182]:61556 helo=[10.0.9.33]) by telf.telf.com with esmtpsa (TLSv1:DHE-RSA-AES256-SHA:256) (Exim 4.80.1) (envelope-from ) id 1bjv8U-0001Gl-8s for gdb-patches@sourceware.org; Tue, 13 Sep 2016 17:20:58 -0400 From: Giah de Barag Subject: Restored Objective-C language support Message-Id: <3AC64B67-79FD-4F55-8F6E-3784C3C4A13B@crelg.com> Date: Tue, 13 Sep 2016 17:20:57 -0400 To: GDB Patches Mime-Version: 1.0 (Mac OS X Mail 9.3 \(3124\)) X-Get-Message-Sender-Via: telf.telf.com: authenticated_id: gdb@crelg.com 2016-09-13 Giah de Barag 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 = 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 STRING %token NSSTRING /* ObjC Foundation "NSString" literal */ -%token SELECTOR /* ObjC "@selector" pseudo-operator */ +%token SELECTOR /* ObjC "@selector" pseudo-operator */ %token CHAR %token NAME /* BLOCKNAME defined below to give it higher precedence. */ %token 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,