Chcesz się dowiedzieć, w jaki sposób środowisko wykonawcze obsługuje wyjątki?
Przygotuj się na rozczarowanie.
Ponieważ tak się nie dzieje. ObjC nie ma ABI obsługującej wyjątki, tylko SPI, które już znalazłeś. Bez wątpienia odkryłeś również, że wyjątek ABI w Objective-C jest dokładnie taki sam jak C++ exception handling ABI. W tym celu zacznijmy od jakiegoś kodu.
#include <Foundation/Foundation.h>
int main(int argc, char **argv) {
@try {
@throw [NSException exceptionWithName:@"ExceptionalCircumstances" reason:@"Drunk on power" userInfo:nil];
} @catch(...) {
NSLog(@"Catch");
} @finally {
NSLog(@"Finally");
}
}
Run poprzez brzękiem z -ObjC -O3
(i pozbawiony obrzydliwe ilości informacji debugowania) otrzymujemy w ten sposób:
_main: ## @main
push rbp
mov rbp, rsp
push r14
push rbx
mov rdi, qword ptr [rip + L_OBJC_CLASSLIST_REFERENCES_$_]
mov rsi, qword ptr [rip + L_OBJC_SELECTOR_REFERENCES_]
lea rdx, qword ptr [rip + L__unnamed_cfstring_]
lea rcx, qword ptr [rip + L__unnamed_cfstring_2]
xor r8d, r8d
call qword ptr [rip + [email protected]]
mov rdi, rax
call _objc_exception_throw
LBB0_2:
mov rdi, rax
call _objc_begin_catch
lea rdi, qword ptr [rip + L__unnamed_cfstring_4]
xor eax, eax
call _NSLog
call _objc_end_catch
xor ebx, ebx
LBB0_8:
lea rdi, qword ptr [rip + L__unnamed_cfstring_6]
xor eax, eax
call _NSLog
test bl, bl
jne LBB0_10
LBB0_11:
xor eax, eax
pop rbx
pop r14
pop rbp
ret
LBB0_5:
mov rbx, rax
call _objc_end_catch
jmp LBB0_7
LBB0_6:
mov rbx, rax
LBB0_7:
mov rdi, rbx
call _objc_begin_catch
mov bl, 1
jmp LBB0_8
LBB0_12:
mov r14, rax
test bl, bl
je LBB0_14
jmp LBB0_13
LBB0_10:
call _objc_exception_rethrow
jmp LBB0_11
LBB0_16: ## %.thread
mov r14, rax
LBB0_13:
call _objc_end_catch
LBB0_14:
mov rdi, r14
call __Unwind_Resume
LBB0_15:
call _objc_terminate
Jeśli skompilować go z ObjC++ nic się nie zmieni. (Cóż, to nie do końca prawda. Ostatni _objc_terminate
zamienia się w skok w osobistą rutynę Clang'a ___clang_call_terminate
). W każdym razie ten kod można podzielić na 3 ważne sekcje. Pierwszy z nich to _main
na początek LBB0_2
lub tam, gdzie ma miejsce nasz blok try. Ponieważ rażąco rzucając wyjątek i łapie go w naszym bloku try
, kompilator posunął się naprzód i usunął gałąź wokół LBB0_2
i przeniósł się bezpośrednio do uchwytów catch. W tym momencie Objective-C lub dokładniej CoreFoundation ustawił dla nas obiekt wyjątku, a libC++ rozpoczęło wyszukiwanie procedury obsługi wyjątku podczas wymaganej fazy rozwijania.
Drugi ważny blok kodu to LBB0_2
na koniec LBB0_11
, w którym znajdują się nasze bloki catch
i finally
. Ponieważ wszystko jest dobrze, cały kod pod nim jest martwy (i miejmy nadzieję, że zostanie usunięty), ale wyobraźmy sobie, że tak nie było.
Trzecia część pochodzi z LBB0_8
na dole, gdzie kompilator wysłałby skok z NSLog w LBB0_2
, gdybyśmy zrobili coś głupiego, powiedzmy, próbowali nie złapać naszego wyjątku. Ten handler odwraca się nieco po wywołaniu w objc_begin_catch
, co powoduje, że rozgałęziamy się wokół ret
i przechodzimy na objc_exception_rethrow()
, który mówi instruktorowi odwijającemu, że upuszczamy piłkę i kontynuujemy wyszukiwanie handlerów w innym miejscu. Oczywiście, jesteśmy główni, więc nie ma innych uchwytów, a gdy wychodzimy, wywoływane jest std::terminate
.
To wszystko, jeśli chcesz spróbować pisać te rzeczy ręcznie. Wszystkie funkcje __cxa_*
i ObjC SPI rzucają wokół obiektów wyjątków w sposób, na który nie można polegać, i (raczej pesymistycznie wiele) procedur obsługi są emitowane w very tight order, aby upewnić się, że kontrakt ABI ABI jest spełniony, ponieważ jeśli to nie jest specyfikacja specyfikacji std::terminate
Zostać wezwanym.Jeśli chcesz wziąć czynny udział w słuchaniu, możesz mieć redefine the exception handling stuff z własnymi funkcjami, a Objective-C ma objc_setUncaughtExceptionHandler
, objc_setExceptionMatcher
objc_setExceptionPreprocessor
.
Biorąc pod uwagę obj-C++, czy chcesz też samodzielnie rozwijać stos? – hamstergene
Eugene, nie jestem pewien, czy podążam ... Szukam przykładu takiego jak UncaughtException, którego udało mi się wymyślić tutaj: https://gist.github.com/1073294#file_uncaught_exception. m, ale domyślam się czegoś bardziej przypominającego rodzaj "begin_try" i "end_try". – TooTallNate
Zestawienie bloku try/catch rzeczywiście pokazuje połączenia z objc_begin_catch i objc_end_catch. Czy próbowałeś przez to przejrzeć, aby zobaczyć, jak są wywoływane? –