seh_try_macros-00.diff (finally, __finally!)

Greg Turner gmturner007 at ameritech.net
Sun Jan 12 19:21:01 CST 2003


This implements __try, __finally, and __except, using Ove's clever
suggestion of macro-abuse with lexically-scoped function definitions.
It's incomplete, wasteful, confusing, etc, but it ought, at least,
to allow us to parse MIDL-generated code, and compile some code expecting
these to be there as a compiler extension.

Note that, since these are not built in to the compiler, you still
need to #include <wine/exception.h> to use them!  They also require
some symbols to be found in ntdll.dll.so, so you must link with
ntdll if you use the macros in your code.  It's totally untested,
but this ought to support GetExceptionCode in the expression which
is fed to except. (Guess I should document this stuff somewhere).

There are some fixme's in there where I need to store the
"contents" of the exception in memory, and later (out of scope)
retrieve that memory turn it back into an exception.  Instead of doing so,
I just remembered whether or not to raise /some/ exception and used
assert... perhaps somebody knows the right way to "marshal"
an seh excption (exsehption? ... :( bad joke, sorry) into memory so
it can effectively be re-raised?

Allright, enough yackin' ... here goes:

LICENSE: X11/bugroff

CHANGELOG: 
* dlls/ntdll: exception.c, ntdll.spec;
  dlls/ntdll/tests: seh_macros.c (new), Makefile.in;
  include/wine: exception.h:
- implement workalikes of __try, __finally, and __except
  macros using wine's __TRY, __FINALLY, and __EXCEPT,
  and magical ({ }) incantations.  No __leave yet.
- some basic tests of the new macros.  Testing these on win32
  should indicate whether I really got the semantics right or not.

--
diff -ur -x CVS -x 'bigdif*' -x autom4te.cache ../wine.test/dlls/ntdll/exception.c ./dlls/ntdll/exception.c
--- ../wine.test/dlls/ntdll/exception.c	2002-12-13 21:17:20.000000000 -0600
+++ ./dlls/ntdll/exception.c	2003-01-12 17:26:31.000000000 -0600
@@ -54,6 +54,58 @@
 # error You must define GET_IP for this CPU
 #endif
 
+#define WINE_SEH_F_STACKMAX 50
+
+typedef struct _wine_seh_f_data {
+  int loop;
+  /* FIXME: we need an array of structs describing the exceptions, not booleans */
+  int stackdata[WINE_SEH_F_STACKMAX];
+  int stackdepth;
+} wine_seh_f_data;
+
+/* tls globals*/
+wine_seh_f_data *wine_SEH_FData;
+static DWORD wine_seh_f_tls_index = -1;
+
+void wine_init_thread_seh_f()
+{
+    if (wine_seh_f_tls_index == -1)
+        if ((wine_seh_f_tls_index = TlsAlloc()) == -1) assert(FALSE);
+    if ((wine_SEH_FData = (wine_seh_f_data *) TlsGetValue(wine_seh_f_tls_index)) == 0)
+        if (GetLastError())
+            assert(FALSE);
+    if (!wine_SEH_FData) {
+        wine_SEH_FData = (wine_seh_f_data *) LocalAlloc(LPTR, sizeof(wine_seh_f_data));
+        assert(wine_SEH_FData);
+        if (!(TlsSetValue(wine_seh_f_tls_index, (LPVOID) wine_SEH_FData))) assert (FALSE);
+    }
+}
+
+int __wine_get_seh_f_loop()
+{ 
+    wine_init_thread_seh_f();
+    return wine_SEH_FData->loop;
+}
+
+void __wine_set_seh_f_loop(int val) {
+    wine_init_thread_seh_f();
+    wine_SEH_FData->loop = val;
+}
+
+void __wine_push_seh_f_reraise(int val)
+{
+    wine_init_thread_seh_f();
+    assert((wine_SEH_FData->stackdepth) < WINE_SEH_F_STACKMAX);
+    wine_SEH_FData->stackdata[wine_SEH_FData->stackdepth++] = val;
+}
+
+int __wine_pop_seh_f_reraise()
+{
+    wine_init_thread_seh_f();
+    assert((wine_SEH_FData->stackdepth) > 0);
+    return wine_SEH_FData->stackdata[--wine_SEH_FData->stackdepth];
+}
+
 extern void SIGNAL_Unblock(void);
 
 void WINAPI EXC_RtlRaiseException( PEXCEPTION_RECORD, PCONTEXT );
@@ -62,6 +114,7 @@
 void WINAPI EXC_NtRaiseException( PEXCEPTION_RECORD, PCONTEXT,
                                   BOOL, PCONTEXT );
 
+
 /*******************************************************************
  *         EXC_RaiseHandler
  *
diff -ur -x CVS -x 'bigdif*' -x autom4te.cache ../wine.test/dlls/ntdll/ntdll.spec ./dlls/ntdll/ntdll.spec
--- ../wine.test/dlls/ntdll/ntdll.spec	2002-12-12 08:07:29.000000000 -0600
+++ ./dlls/ntdll/ntdll.spec	2003-01-12 17:26:31.000000000 -0600
@@ -1029,6 +1029,10 @@
 # Exception handling
 @ cdecl -norelay __wine_exception_handler(ptr ptr ptr ptr) __wine_exception_handler
 @ cdecl -norelay __wine_finally_handler(ptr ptr ptr ptr) __wine_finally_handler
+@ cdecl -norelay __wine_get_seh_f_loop() __wine_get_seh_f_loop
+@ cdecl -norelay __wine_set_seh_f_loop(long) __wine_set_seh_f_loop
+@ cdecl -norelay __wine_push_seh_f_reraise(long) __wine_push_seh_f_reraise
+@ cdecl -norelay __wine_pop_seh_f_reraise() __wine_pop_seh_f_reraise
 
 # Relays
 @ cdecl -norelay -i386 __wine_call_from_32_regs() __wine_call_from_32_regs
diff -ur -x CVS -x 'bigdif*' -x autom4te.cache ../wine.test/dlls/ntdll/tests/Makefile.in ./dlls/ntdll/tests/Makefile.in
--- ../wine.test/dlls/ntdll/tests/Makefile.in	2002-11-12 15:57:29.000000000 -0600
+++ ./dlls/ntdll/tests/Makefile.in	2003-01-12 17:27:11.000000000 -0600
@@ -9,7 +9,8 @@
 	error.c \
 	generated.c \
 	rtlbitmap.c \
-	rtlstr.c
+	rtlstr.c \
+	seh_macros.c
 
 @MAKE_TEST_RULES@
 
diff -ur -x CVS -x 'bigdif*' -x autom4te.cache ../wine.test/include/wine/exception.h ./include/wine/exception.h
--- ../wine.test/include/wine/exception.h	2003-01-03 19:32:52.000000000 -0600
+++ ./include/wine/exception.h	2003-01-12 17:42:02.000000000 -0600
@@ -67,6 +67,8 @@
  */
 #ifdef USE_COMPILER_EXCEPTIONS
 
+#include <assert.h>
+
 #define __TRY __try
 #define __EXCEPT(func) __except((func)(GetExceptionInformation()))
 #define __FINALLY(func) __finally { (func)(!AbnormalTermination()); }
@@ -146,6 +148,63 @@
 extern DWORD __wine_finally_handler( PEXCEPTION_RECORD record, EXCEPTION_FRAME *frame,
                                      CONTEXT *context, LPVOID pdispatcher );
 
+/*
+ * now we can implement the __try/__except/__finally magic of VC++ as standard (?) macros.
+ * this is a "good enough" implementation, it's incomplete (i.e.: no __leave yet, no break in
+ * __try clauses, and who knows what else...); but should suffice for most uses.
+ */
+
+/* these functions simulate a global thread-local variable */
+extern int __wine_get_seh_f_loop(); 
+extern void __wine_set_seh_f_loop(int);
+/* these functions implement a TLS stack of elements (currently int's). */
+/* FIXME: push/pop an exception info struct instead of an int */
+extern int __wine_pop_seh_f_reraise();
+extern void __wine_push_seh_f_reraise(int);
+
+#define __try \
+  \
+  if ( ({ \
+    int seh_result = -2; \
+    static WINE_EXCEPTION_FILTER(seh_handler); \
+    __TRY {
+
+#define __finally \
+    \
+    } __EXCEPT(seh_handler) { \
+    } __ENDTRY \
+    WINE_EXCEPTION_FILTER(seh_handler) { \
+      /* FIXME: remember exception info */ \
+      seh_result = -4; \
+      __wine_push_seh_f_reraise(1); \
+      return(1); \
+    } \
+    if (seh_result != -4) __wine_push_seh_f_reraise(0); \
+  0; }) ) { } else \
+    /* FIXME: this could be done more efficiently */ \
+    for (__wine_set_seh_f_loop(0); __wine_get_seh_f_loop() <= 1; __wine_set_seh_f_loop(__wine_get_seh_f_loop() + 1)) \
+      if (__wine_get_seh_f_loop() == 1) { \
+        if (__wine_pop_seh_f_reraise()) { \
+          /* FIXME: re-raise from stored exception info */ assert(FALSE); \
+	} else { \
+	  /* do nothing */ \
+	} \
+      } else
+
+#define __except(...) \
+   \
+    } __EXCEPT(seh_handler) { \
+      seh_result = -3; \
+    } __ENDTRY \
+    WINE_EXCEPTION_FILTER(seh_handler) { \
+      seh_result = (__VA_ARGS__); \
+      return(seh_result); \
+    } \
+  (seh_result != -3); }) ) { } else
+
+/* FIXME */
+#define __leave assert(FALSE)
+
 #endif /* USE_COMPILER_EXCEPTIONS */
 
 static inline EXCEPTION_FRAME * WINE_UNUSED __wine_push_frame( EXCEPTION_FRAME *frame )
--- /dev/null	1969-12-31 18:00:00.000000000 -0600
+++ ./dlls/ntdll/tests/seh_macros.c	2003-01-12 17:28:39.000000000 -0600
@@ -0,0 +1,273 @@
+/*
+ * Unit test suite for __try, __finally, __except and __leave macros
+ *
+ * Copyright 2002 Greg Turner
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <assert.h>
+#include "wine/test.h"
+#include <winbase.h>
+#include <winnt.h>
+#include <winerror.h>
+#include "wine/exception.h"
+
+void TryMacros()
+{
+    int x;
+
+    trace("beginning TryMacros:\n");
+    trace("  basic no-exception-handling macro test\n");
+
+    x = 0;
+    __try {
+       x = 1; 
+    } __except( ok(FALSE, "Expression shouldn't run."), 1 ) {
+      ok(FALSE, "Except clause shouldn't run.");
+    }
+
+    trace("  handle exception:\n");
+
+    x = 0;
+    __try {
+      x++;
+      x = (int)(*(int **)0);
+      x++; /* never executed */
+    } __except( ( (x==1) ? ( ok(FALSE, "x should be 1 was %d",x) ) : (x=3) ), 1) {
+      ok((x==3), "x should be 3 was %d", x);
+      if (x==3) x = 4;
+    }
+    ok((x==4), "x should be 4 was %d", x);
+
+    trace("  twice nested exceptions test: no exceptions\n");
+
+    x = 0;
+    __try {
+      ok((x==0), "x should be 0 was %d", x);
+      if (x==0) x = 1;
+      __try {
+        ok((x==1), "x should be 1, was %d", x);
+	if (x==1) x = 2;
+      } __except(0) {
+        ok(FALSE, "exception handler code shouldn't run");
+	x = -99;
+	assert(FALSE);
+      }
+      ok((x==2), "x should be 2 was %d", x);
+      if (x==2) x = 3;
+    } __except(1) {
+      ok(FALSE, " exception handler code shouldn't run");
+      x = -98;
+    }
+
+    ok((x==3), "x should be 3 was %d", x);
+
+    trace("  twice nested exceptions test with exceptions\n");
+
+    x = 0;
+    __try {
+      __try {
+        ok((x==0), "x should be 0 was %d", x);
+	if (x==0) x = 1;
+	assert(FALSE);
+      } __except(1) {
+        ok((x==1), "x should be 1 was %d", x);
+	if (x==1) x = 2;
+	assert(FALSE);
+      }
+      ok(FALSE, "this code should be skipped");
+    } __except(1) {
+      ok ((x==2), "x should be 2 was %d", x);
+      if (x==2) x = 3;
+      __try {
+        ok ((x==3), "x should be 3 was %d", x);
+	if (x==3) x = 4;
+	assert(FALSE);
+      } __except(1) {
+        ok ((x==4), "x should be 4 was %d", x);
+	if (x==4) x = 5;
+      }
+      ok ((x==5), "x should be 5 was %d", x);
+      if (x==5) x = 6;
+    }
+    ok ((x==6), "x should be 6 was %d", x);
+
+    trace("  absurdly nested tryblocks test with no exceptions\n");
+
+    x = 0;
+    #define nesttry1(x) __try {x++; } __except(1) { x=-1000; }
+    #define nesttry2(x) __try {x++; nesttry1(x) } __except(1) { x=-1000; nesttry1(x) }
+    #define nesttry3(x) __try {x++; nesttry2(x) } __except(1) { x=-1000; nesttry2(x) }
+    #define nesttry4(x) __try {x++; nesttry3(x) } __except(1) { x=-1000; nesttry3(x) }
+    #define nesttry5(x) __try {x++; nesttry4(x) } __except(1) { x=-1000; nesttry4(x) }
+    #define nesttry6(x) __try {x++; nesttry5(x) } __except(1) { x=-1000; nesttry5(x) }
+    #define nesttry7(x) __try {x++; nesttry6(x) } __except(1) { x=-1000; nesttry6(x) }
+    nesttry7(x)
+    #undef nesttry7
+    #undef nesttry6
+    #undef nesttry5
+    #undef nesttry4
+    #undef nesttry3
+    #undef nesttry2
+    #undef nesttry1
+    ok((x==7), "x should be 7 was %d", x);
+
+    trace("  absurdly nested tryblocks test with exceptions\n");
+
+    x = 0;
+    #define nesttry1(x) __try {x++; } __except(1) { x++; assert(FALSE); }          
+    #define nesttry2(x) __try {x++; nesttry1(x) } __except(1) { x++; nesttry1(x); assert(FALSE); }
+    #define nesttry3(x) __try {x++; nesttry2(x) } __except(1) { x++; nesttry2(x); assert(FALSE); }
+    #define nesttry4(x) __try {x++; nesttry3(x) } __except(1) { x++; nesttry3(x); assert(FALSE); }
+    #define nesttry5(x) __try {x++; nesttry4(x) } __except(1) { x++; nesttry4(x); assert(FALSE); }
+    #define nesttry6(x) __try {x++; nesttry5(x) } __except(1) { x++; nesttry5(x); assert(FALSE); }
+    #define nesttry7(x) __try {x++; nesttry6(x); assert(FALSE); } __except(1) { x++; nesttry6(x); assert(FALSE); } 
+    __try {
+      nesttry7(x)
+    } __except (1) {
+    }
+    #undef nesttry7
+    #undef nesttry6
+    #undef nesttry5
+    #undef nesttry4
+    #undef nesttry3
+    #undef nesttry2
+    #undef nesttry1
+    ok((x==14), "x should be 14 was %d", x);
+
+    trace("  basic finally test (no exception)\n");
+    
+    x = 0;
+    __try {
+      x++;
+    } __finally {
+      x++;
+    }
+    ok((x==2), "x should be 2 was %d", x);
+
+    trace("  repeat basic finally test (no exception)\n");
+
+    x = 0;
+    __try {
+      x++;
+    } __finally {
+      x++;
+    }
+    ok((x==2), "x should be 2 was %d", x);
+
+    trace("  basic finally test (with exception)\n");
+
+    x = 0;
+    __try {
+      __try {
+        x++;
+	assert(FALSE);
+        x++;
+      } __finally {
+        ok((x==1), "x should be 1 was %d", x);
+        if (x==1) x = 2;
+      }
+    } __except (1) {
+      ok((x==2), "x should be 2 was %d", x);
+      if (x==2) x = 3;
+    }
+    ok((x==3), "x should be 3 was %d", x);
+
+    trace("  nested finallys (no exception)\n");
+
+    x = 0;
+    __try {
+      x++;
+      __try {
+        x++;
+      } __finally {
+        x++;
+      }
+      x++;
+    } __finally {
+      x++;
+    }
+    x++;
+    ok((x==6), "x should be 6 was %d", x);
+
+    trace("  nested finallys (with inner exception)\n");
+
+    x = 0;
+    __try {
+      ok((x==0), "x should be 0 was %d", x);
+      if (x==0) x = 1;
+      __try {
+        ok((x==1), "x should be 1 was %d", x);
+	if (x==1) x = 2;
+        __try {
+	  ok((x==2), "x should be 2 was %d", x);
+	  if (x==2) x = 3;
+	  assert(FALSE);
+	  ok(FALSE, "this code should not execute");
+	} __finally {
+	  ok((x==3), "x should be 3 was %d", x);
+	  if (x==3) x = 4;
+	}
+	ok(FALSE, "this code should not execute");
+      } __finally {
+        ok((x==4), "x should be 4 was %d", x);
+	if (x==4) x = 5;
+      }
+      ok(FALSE, "this code should not execute");
+    } __except (1) {
+      ok((x==5), "x should be 5 was %d", x);
+      if (x==5) x = 6;
+    }
+    ok((x==6), "x should be 6 was %d", x);
+
+    trace("  nested finallys (with outer exception)\n");
+
+    x = 0;
+    __try {
+      ok((x==0), "x should be 0 was %d", x);
+      if (x==0) x = 1;
+      __try {
+        ok((x==1), "x should be 1 was %d", x);
+	if (x==1) x = 2;
+        __try {
+	  ok((x==2), "x should be 2 was %d", x);
+	  if (x==2) x = 3;
+	} __finally {
+	  ok((x==3), "x should be 3 was %d", x);
+	  if (x==3) x = 4;
+	}
+	assert(FALSE);
+	ok(FALSE, "this code should not execute");
+      } __finally {
+        ok((x==4), "x should be 4 was %d", x);
+	if (x==4) x = 5;
+      }
+      ok(FALSE, "this code should not execute");
+    } __except (1) {
+      ok((x==5), "x should be 5 was %d", x);
+      if (x==5) x = 6;
+    }
+    ok((x==6), "x should be 6 was %d", x);
+
+ 
+    trace("End.\n");
+}
+
+START_TEST( seh_macros )
+{
+    trace ( " ** __try/__except/__finally Macro Tests **\n" );
+    TryMacros();
+}
-- 
gmt

"If everyone is thinking alike then somebody isn't
thinking." --George S. Patton




More information about the wine-patches mailing list