From patchwork Mon Feb 9 23:20:46 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pedro Alves X-Patchwork-Id: 5005 Received: (qmail 13795 invoked by alias); 9 Feb 2015 23:22:02 -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 13739 invoked by uid 89); 9 Feb 2015 23:22:01 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-2.0 required=5.0 tests=AWL, BAYES_00, SPF_HELO_PASS, SPF_PASS, T_RP_MATCHES_RCVD autolearn=ham version=3.3.2 X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES256-GCM-SHA384 encrypted) ESMTPS; Mon, 09 Feb 2015 23:21:59 +0000 Received: from int-mx14.intmail.prod.int.phx2.redhat.com (int-mx14.intmail.prod.int.phx2.redhat.com [10.5.11.27]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id t19NLwSN006176 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL) for ; Mon, 9 Feb 2015 18:21:58 -0500 Received: from brno.lan (ovpn01.gateway.prod.ext.ams2.redhat.com [10.39.146.11]) by int-mx14.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id t19NKkv1026307 for ; Mon, 9 Feb 2015 18:21:57 -0500 From: Pedro Alves To: gdb-patches@sourceware.org Subject: [PATCH 36/36] Make TRY/CATCH use real C++ try/catch in C++ mode Date: Mon, 9 Feb 2015 23:20:46 +0000 Message-Id: <1423524046-20605-37-git-send-email-palves@redhat.com> In-Reply-To: <1423524046-20605-1-git-send-email-palves@redhat.com> References: <1423524046-20605-1-git-send-email-palves@redhat.com> Although the current TRY/CATCH implementation works in C++ mode too, it relies on setjmp/longjmp, and longjmp bypasses calling the destructors of objects on the stack, which is obviously bad for C++. This patch fixes this by makes TRY/CATCH use real try/catch in C++ mode behind the scenes. The way this is done allows RAII and cleanups to coexist while we phase out cleanups, instead of requiring a flag day. This patch is not strictly necessary until we require a C++ compiler and start actually using RAII, though I'm all for baby steps, and it shows my proposed way forward. I think we should put it in now, to allow easier experimentation and exposure of potential problems with real C++ exceptions. gdb/ChangeLog: 2015-02-09 Pedro Alves * common/common-exceptions.c [!__cplusplus] (enum catcher_state) (exceptions_state_mc_action_iter) (exceptions_state_mc_action_iter_1, exceptions_state_mc_catch): Don't define. [__cplusplus] (try_scope_depth): New global. [__cplusplus] (exception_try_scope_entry) (exception_try_scope_exit, gdb_exception_sliced_copy) (exception_rethrow): New functions. (throw_exception): In C++ mode, throw gdb_exception_RETURN_MASK_QUIT for RETURN_QUIT and gdb_exception_RETURN_MASK_ERROR for RETURN_ERROR. (throw_it): In C++ mode, use try_scope_depth. * common/common-exceptions.h [!__cplusplus] (exceptions_state_mc_action_iter) (exceptions_state_mc_action_iter_1, exceptions_state_mc_catch): Don't declare. [__cplusplus] (exception_try_scope_entry) (exception_try_scope_exit, exception_rethrow): Declare. [__cplusplus] (struct exception_try_scope): New struct. [__cplusplus] (TRY, CATCH, END_CATCH): Reimplement on top of real C++ exceptions. (struct gdb_exception_RETURN_MASK_ALL) (struct gdb_exception_RETURN_MASK_ERROR) (struct gdb_exception_RETURN_MASK_QUIT): New types. --- gdb/common/common-exceptions.c | 77 +++++++++++++++++++++++++++++++++++++++++- gdb/common/common-exceptions.h | 67 ++++++++++++++++++++++++++++++++++++ 2 files changed, 143 insertions(+), 1 deletion(-) diff --git a/gdb/common/common-exceptions.c b/gdb/common/common-exceptions.c index 2ad0ce1..b300a9d 100644 --- a/gdb/common/common-exceptions.c +++ b/gdb/common/common-exceptions.c @@ -22,6 +22,8 @@ const struct gdb_exception exception_none = { 0, GDB_NO_ERROR, NULL }; +#ifndef __cplusplus + /* Possible catcher states. */ enum catcher_state { /* Initial state, a new catcher has just been created. */ @@ -185,7 +187,7 @@ exceptions_state_mc_catch (struct gdb_exception *exception, { if (mask & RETURN_MASK (exception->reason)) { - /* Exit normally and let the called handle the + /* Exit normally and let the caller handle the exception. */ return 1; } @@ -211,6 +213,56 @@ exceptions_state_mc_action_iter_1 (void) return exceptions_state_mc (CATCH_ITER_1); } +#else /* !__cplusplus */ + +/* How many nested TRY blocks we have. See exception_messages and + throw_it. */ + +static int try_scope_depth; + +/* Called on entry to a TRY scope. */ + +void * +exception_try_scope_entry (void) +{ + ++try_scope_depth; + return (void *) save_cleanups (); +} + +/* Called on exit of a TRY scope, either normal exit or exception + exit. */ + +void +exception_try_scope_exit (void *saved_state) +{ + restore_cleanups ((struct cleanup *) saved_state); + --try_scope_depth; +} + +/* Called by the default catch block. IOW, we'll get here before + jumping out to the next outermost scope an exception if a GDB + exception is not caught. */ + +void +exception_rethrow (void) +{ + /* Run this scope's cleanups before re-throwing to the next + outermost scope. */ + prepare_to_throw_exception (); + do_cleanups (all_cleanups ()); + throw; +} + +/* Copy the 'gdb_exception' portion of FROM to TO. */ + +static void +gdb_exception_sliced_copy (struct gdb_exception *to, const struct gdb_exception *from) +{ + *to = *from; +} + +#endif /* !__cplusplus */ + /* Return EXCEPTION to the nearest containing catch_errors(). */ void @@ -220,12 +272,31 @@ throw_exception (struct gdb_exception exception) do_cleanups (all_cleanups ()); +#ifndef __cplusplus /* Jump to the containing catch_errors() call, communicating REASON to that call via setjmp's return value. Note that REASON can't be zero, by definition in defs.h. */ exceptions_state_mc (CATCH_THROWING); current_catcher->exception = exception; SIGLONGJMP (current_catcher->buf, exception.reason); +#else + if (exception.reason == RETURN_QUIT) + { + gdb_exception_RETURN_MASK_QUIT ex; + + gdb_exception_sliced_copy (&ex, &exception); + throw ex; + } + else if (exception.reason == RETURN_ERROR) + { + gdb_exception_RETURN_MASK_ERROR ex; + + gdb_exception_sliced_copy (&ex, &exception); + throw ex; + } + else + gdb_assert_not_reached ("invalid return reason"); +#endif } /* A stack of exception messages. @@ -249,7 +320,11 @@ throw_it (enum return_reason reason, enum errors error, const char *fmt, { struct gdb_exception e; char *new_message; +#ifndef __cplusplus int depth = catcher_list_size (); +#else + int depth = try_scope_depth; +#endif gdb_assert (depth > 0); diff --git a/gdb/common/common-exceptions.h b/gdb/common/common-exceptions.h index d2c0bee..2e6a6d9 100644 --- a/gdb/common/common-exceptions.h +++ b/gdb/common/common-exceptions.h @@ -121,10 +121,16 @@ struct gdb_exception the exceptions subsystem and not used other than via the TRY/CATCH macros defined below. */ +#ifndef __cplusplus extern SIGJMP_BUF *exceptions_state_mc_init (void); extern int exceptions_state_mc_action_iter (void); extern int exceptions_state_mc_action_iter_1 (void); extern int exceptions_state_mc_catch (struct gdb_exception *, int); +#else +extern void *exception_try_scope_entry (void); +extern void exception_try_scope_exit (void *saved_state); +extern void exception_rethrow (void); +#endif /* Macro to wrap up standard try/catch behavior. @@ -151,6 +157,8 @@ extern int exceptions_state_mc_catch (struct gdb_exception *, int); */ +#ifndef __cplusplus + #define TRY \ { \ SIGJMP_BUF *buf = \ @@ -168,6 +176,65 @@ extern int exceptions_state_mc_catch (struct gdb_exception *, int); #define END_CATCH \ } +#else + +/* Prevent error/quit during TRY from calling cleanups established + prior to here. This pops out the scope in either case of normal + exit or exception exit. */ +struct exception_try_scope +{ + exception_try_scope () + { + saved_state = exception_try_scope_entry (); + } + ~exception_try_scope () + { + exception_try_scope_exit (saved_state); + } + + void *saved_state; +}; + +/* We still need to wrap TRY/CATCH in C++ so that cleanups and C++ + exceptions can coexist. The TRY blocked is wrapped in a + do/while(0) so that break/continue within the block works the same + as in C. */ +#define TRY \ + try \ + { \ + exception_try_scope exception_try_scope_instance; \ + do \ + { + +#define CATCH(EXCEPTION, MASK) \ + } while (0); \ + } \ + catch (struct gdb_exception ## _ ## MASK &EXCEPTION) + +#define END_CATCH \ + catch (...) \ + { \ + exception_rethrow (); \ + } + +/* The exception types client code may catch. They're just shims + around gdb_exception that add nothing but type info. Which is used + is selected depending on the MASK argument passed to CATCH. */ + +struct gdb_exception_RETURN_MASK_ALL : public gdb_exception +{ +}; + +struct gdb_exception_RETURN_MASK_ERROR : public gdb_exception_RETURN_MASK_ALL +{ +}; + +struct gdb_exception_RETURN_MASK_QUIT : public gdb_exception_RETURN_MASK_ALL +{ +}; + +#endif + /* *INDENT-ON* */ /* Hook to allow client-specific actions to be performed prior to