--- /dev/null
+#include "test.h"
+#include <objc/runtime.h>
+#include <objc/objc-exception.h>
+
+static volatile int state = 0;
+#define BAD 1000000
+
+#if defined(USE_FOUNDATION)
+
+#include <Foundation/Foundation.h>
+
+static NSAutoreleasePool *p;
+void pool(void) { [p release]; p = [NSAutoreleasePool new]; }
+
+@interface Super : NSException @end
+@implementation Super
++new { return [[self exceptionWithName:@"Super" reason:@"reason" userInfo:nil] retain]; }
+-(void)check { state++; }
++(void)check { testassert(!"caught class object, not instance"); }
+@end
+
+#else
+
+void pool(void) { }
+
+@interface Super { id isa; } @end
+@implementation Super
++new { return class_createInstance(self, 0); }
++(void)initialize { }
+-(void)check { state++; }
++(void)check { testassert(!"caught class object, not instance"); }
+-(void)release { object_dispose(self); }
+@end
+
+#endif
+
+@interface Sub : Super @end
+@implementation Sub
+@end
+
+
+#if __OBJC2__
+
+void altHandlerFail(id unused __unused, void *context __unused)
+{
+ fail("altHandlerFail called");
+}
+
+#define ALT_HANDLER(n) \
+ void altHandler##n(id unused __unused, void *context) \
+ { \
+ testassert(context == (void*)&altHandler##n); \
+ testassert(state == n); \
+ state++; \
+ }
+
+ALT_HANDLER(2)
+ALT_HANDLER(3)
+ALT_HANDLER(4)
+ALT_HANDLER(5)
+ALT_HANDLER(6)
+ALT_HANDLER(7)
+
+
+static void throwWithAltHandler(void) __attribute__((noinline));
+static void throwWithAltHandler(void)
+{
+ @try {
+ state++;
+ uintptr_t token = objc_addExceptionHandler(altHandler3, altHandler3);
+ // state++ inside alt handler
+ @throw [Super new];
+ state = BAD;
+ objc_removeExceptionHandler(token);
+ }
+ @catch (Sub *e) {
+ state = BAD;
+ }
+ state = BAD;
+}
+
+
+static void throwWithAltHandlerAndRethrow(void) __attribute__((noinline));
+static void throwWithAltHandlerAndRethrow(void)
+{
+ @try {
+ state++;
+ uintptr_t token = objc_addExceptionHandler(altHandler3, altHandler3);
+ // state++ inside alt handler
+ @throw [Super new];
+ state = BAD;
+ objc_removeExceptionHandler(token);
+ }
+ @catch (...) {
+ testassert(state == 4);
+ state++;
+ @throw;
+ }
+ state = BAD;
+}
+
+#endif
+
+
+int main()
+{
+ pool();
+
+ testprintf("try-catch-finally, exception caught exactly\n");
+
+ state = 0;
+ @try {
+ state++;
+ @try {
+ state++;
+ @throw [Super new];
+ state = BAD;
+ }
+ @catch (Super *e) {
+ state++;
+ [e check]; // state++
+ [e release];
+ }
+ @finally {
+ state++;
+ }
+ state++;
+ }
+ @catch (...) {
+ state = BAD;
+ }
+ testassert(state == 6);
+
+
+ testprintf("try-finally, no exception thrown\n");
+
+ state = 0;
+ @try {
+ state++;
+ @try {
+ state++;
+ }
+ @finally {
+ state++;
+ }
+ state++;
+ }
+ @catch (...) {
+ state = BAD;
+ }
+ testassert(state == 4);
+
+
+ testprintf("try-finally, with exception\n");
+
+ state = 0;
+ @try {
+ state++;
+ @try {
+ state++;
+ @throw [Super new];
+ state = BAD;
+ }
+ @finally {
+ state++;
+ }
+ state = BAD;
+ }
+ @catch (id e) {
+ state++;
+ [e check]; // state++
+ [e release];
+ }
+ testassert(state == 5);
+
+
+ testprintf("try-catch-finally, no exception\n");
+
+ state = 0;
+ @try {
+ state++;
+ @try {
+ state++;
+ }
+ @catch (...) {
+ state = BAD;
+ }
+ @finally {
+ state++;
+ }
+ state++;
+ } @catch (...) {
+ state = BAD;
+ }
+ testassert(state == 4);
+
+
+ testprintf("try-catch-finally, exception not caught\n");
+
+ state = 0;
+ @try {
+ state++;
+ @try {
+ state++;
+ @throw [Super new];
+ state = BAD;
+ }
+ @catch (Sub *e) {
+ state = BAD;
+ }
+ @finally {
+ state++;
+ }
+ state = BAD;
+ }
+ @catch (id e) {
+ state++;
+ [e check]; // state++
+ [e release];
+ }
+ testassert(state == 5);
+
+
+ testprintf("try-catch-finally, exception caught exactly, rethrown\n");
+
+ state = 0;
+ @try {
+ state++;
+ @try {
+ state++;
+ @throw [Super new];
+ state = BAD;
+ }
+ @catch (Super *e) {
+ state++;
+ [e check]; // state++
+ @throw;
+ state = BAD;
+ }
+ @finally {
+ state++;
+ }
+ state = BAD;
+ }
+ @catch (id e) {
+ state++;
+ [e check]; // state++
+ [e release];
+ }
+ testassert(state == 7);
+
+
+ testprintf("try-catch, no exception\n");
+
+ state = 0;
+ @try {
+ state++;
+ @try {
+ state++;
+ }
+ @catch (...) {
+ state = BAD;
+ }
+ state++;
+ } @catch (...) {
+ state = BAD;
+ }
+ testassert(state == 3);
+
+
+ testprintf("try-catch, exception not caught\n");
+
+ state = 0;
+ @try {
+ state++;
+ @try {
+ state++;
+ @throw [Super new];
+ state = BAD;
+ }
+ @catch (Sub *e) {
+ state = BAD;
+ }
+ state = BAD;
+ }
+ @catch (id e) {
+ state++;
+ [e check]; // state++
+ [e release];
+ }
+ testassert(state == 4);
+
+
+ testprintf("try-catch, exception caught exactly\n");
+
+ state = 0;
+ @try {
+ state++;
+ @try {
+ state++;
+ @throw [Super new];
+ state = BAD;
+ }
+ @catch (Super *e) {
+ state++;
+ [e check]; // state++
+ [e release];
+ }
+ state++;
+ }
+ @catch (...) {
+ state = BAD;
+ }
+ testassert(state == 5);
+
+
+ testprintf("try-catch, exception caught exactly, rethrown\n");
+
+ state = 0;
+ @try {
+ state++;
+ @try {
+ state++;
+ @throw [Super new];
+ state = BAD;
+ }
+ @catch (Super *e) {
+ state++;
+ [e check]; // state++
+ @throw;
+ state = BAD;
+ }
+ state = BAD;
+ }
+ @catch (id e) {
+ state++;
+ [e check]; // state++
+ [e release];
+ }
+ testassert(state == 6);
+
+
+ testprintf("try-catch, exception caught exactly, thrown again explicitly\n");
+
+ state = 0;
+ @try {
+ state++;
+ @try {
+ state++;
+ @throw [Super new];
+ state = BAD;
+ }
+ @catch (Super *e) {
+ state++;
+ [e check]; // state++
+ @throw e;
+ state = BAD;
+ }
+ state = BAD;
+ }
+ @catch (id e) {
+ state++;
+ [e check]; // state++
+ [e release];
+ }
+ testassert(state == 6);
+
+
+ testprintf("try-catch, default catch, rethrown\n");
+
+ state = 0;
+ @try {
+ state++;
+ @try {
+ state++;
+ @throw [Super new];
+ state = BAD;
+ }
+ @catch (...) {
+ state++;
+ @throw;
+ state = BAD;
+ }
+ state = BAD;
+ }
+ @catch (id e) {
+ state++;
+ [e check]; // state++
+ [e release];
+ }
+ testassert(state == 5);
+
+
+ testprintf("try-catch, default catch, rethrown and caught inside nested handler\n");
+
+ state = 0;
+ @try {
+ state++;
+ @try {
+ state++;
+ @throw [Super new];
+ state = BAD;
+ }
+ @catch (...) {
+ state++;
+
+ @try {
+ state++;
+ @throw;
+ state = BAD;
+ } @catch (Sub *e) {
+ state = BAD;
+ } @catch (Super *e) {
+ state++;
+ [e check]; // state++
+ [e release];
+ } @catch (...) {
+ state = BAD;
+ } @finally {
+ state++;
+ }
+
+ state++;
+ }
+ state++;
+ }
+ @catch (...) {
+ state = BAD;
+ }
+ testassert(state == 9);
+
+
+ testprintf("try-catch, default catch, rethrown inside nested handler but not caught\n");
+
+ state = 0;
+ @try {
+ state++;
+ @try {
+ state++;
+ @throw [Super new];
+ state = BAD;
+ }
+ @catch (...) {
+ state++;
+
+ @try {
+ state++;
+ @throw;
+ state = BAD;
+ } @catch (Sub *e) {
+ state = BAD;
+ } @finally {
+ state++;
+ }
+
+ state = BAD;
+ }
+ state = BAD;
+ }
+ @catch (id e) {
+ state++;
+ [e check]; // state++
+ [e release];
+ }
+ testassert(state == 7);
+
+#if __OBJC2__
+ // alt handlers
+ // run a lot to catch failed unregistration (runtime complains at 1000)
+#define ALT_HANDLER_REPEAT 2000
+ int i;
+
+ testprintf("alt handler, no exception\n");
+
+ for (i = 0; i < ALT_HANDLER_REPEAT; i++) {
+ pool();
+
+ state = 0;
+ @try {
+ state++;
+ @try {
+ uintptr_t token = objc_addExceptionHandler(altHandlerFail, 0);
+ state++;
+ objc_removeExceptionHandler(token);
+ }
+ @catch (...) {
+ state = BAD;
+ }
+ state++;
+ } @catch (...) {
+ state = BAD;
+ }
+ testassert(state == 3);
+ }
+
+ testprintf("alt handler, exception thrown through\n");
+
+ for (i = 0; i < ALT_HANDLER_REPEAT; i++) {
+ pool();
+
+ state = 0;
+ @try {
+ state++;
+ @try {
+ state++;
+ uintptr_t token = objc_addExceptionHandler(altHandler2, altHandler2);
+ // state++ inside alt handler
+ @throw [Super new];
+ state = BAD;
+ objc_removeExceptionHandler(token);
+ }
+ @catch (Sub *e) {
+ state = BAD;
+ }
+ state = BAD;
+ }
+ @catch (id e) {
+ testassert(state == 3);
+ state++;
+ [e check]; // state++
+ [e release];
+ }
+ testassert(state == 5);
+ }
+
+
+ testprintf("alt handler, nested\n");
+
+ for (i = 0; i < ALT_HANDLER_REPEAT; i++) {
+ pool();
+
+ state = 0;
+ @try {
+ state++;
+ @try {
+ state++;
+ // same-level handlers called in FIFO order (not stack-like)
+ uintptr_t token = objc_addExceptionHandler(altHandler4, altHandler4);
+ // state++ inside alt handler
+ uintptr_t token2 = objc_addExceptionHandler(altHandler5, altHandler5);
+ // state++ inside alt handler
+ throwWithAltHandler(); // state += 2 inside
+ state = BAD;
+ objc_removeExceptionHandler(token);
+ objc_removeExceptionHandler(token2);
+ }
+ @catch (id e) {
+ testassert(state == 6);
+ state++;
+ [e check]; // state++;
+ [e release];
+ }
+ state++;
+ }
+ @catch (...) {
+ state = BAD;
+ }
+ testassert(state == 9);
+ }
+
+
+ testprintf("alt handler, nested, rethrows in between\n");
+
+ for (i = 0; i < ALT_HANDLER_REPEAT; i++) {
+ pool();
+
+ state = 0;
+ @try {
+ state++;
+ @try {
+ state++;
+ // same-level handlers called in FIFO order (not stack-like)
+ uintptr_t token = objc_addExceptionHandler(altHandler5, altHandler5);
+ // state++ inside alt handler
+ uintptr_t token2 = objc_addExceptionHandler(altHandler6, altHandler6);
+ // state++ inside alt handler
+ throwWithAltHandlerAndRethrow(); // state += 3 inside
+ state = BAD;
+ objc_removeExceptionHandler(token);
+ objc_removeExceptionHandler(token2);
+ }
+ @catch (...) {
+ testassert(state == 7);
+ state++;
+ @throw;
+ }
+ state = BAD;
+ }
+ @catch (id e) {
+ testassert(state == 8);
+ state++;
+ [e check]; // state++
+ [e release];
+ }
+ testassert(state == 10);
+ }
+
+
+ testprintf("alt handler, exception thrown and caught inside\n");
+
+ for (i = 0; i < ALT_HANDLER_REPEAT; i++) {
+ pool();
+
+ state = 0;
+ @try {
+ state++;
+ uintptr_t token = objc_addExceptionHandler(altHandlerFail, 0);
+ @try {
+ state++;
+ @throw [Super new];
+ state = BAD;
+ }
+ @catch (Super *e) {
+ state++;
+ [e check]; // state++
+ [e release];
+ }
+ state++;
+ objc_removeExceptionHandler(token);
+ }
+ @catch (...) {
+ state = BAD;
+ }
+ testassert(state == 5);
+ }
+
+#endif
+
+#if defined(USE_FOUNDATION)
+ [p release];
+ succeed("nsexc.m");
+#else
+ succeed("exc.m");
+#endif
+}