Try catch finally is the most commonly used exception handling process. We all know that when a try block code is executed, an exception will be caught by the catch to execute the Catch Block Code, the finally block code is executed no matter how it is. But what if we add the return statement, return and finally execution sequence to the try block?
Finally before return ??
If you have tried this or the finally will always be executed, you will think that finally will be executed before return. However, let's look at the example below.
JS Code:
function testtry() { var i = 0; try { i = 1; return i; } catch (e) { i = 2; return i; } finally { i = 3; } }
. Net code:
private Int32 TestTry() { Int32 i = 0; try { i = 1; return i; } catch { i = 2; return i; } finally { i = 3; } }
Should the result be 1 or 3? If finally is before return, it should be 3, but the above two pieces of code are executed as a result: 1.
Does a function or method return a return directly, and finally is not executed at all ?? Isn't this a conflict with the role that finally will always be executed?
Is finally executed?
Read this Code:
function testtry() { var i = 0; try { i = 1; return i; } catch (e) { i = 2; return i; } finally { i = 3; return i; } }
Because. Net does not allow adding return in finally, the. NET version of this Code is not available.
This JS Code contains a return value than the previous one. What is the result? 1 or 3?
The answer is 3. What does this mean?It indicates that, regardless of the order in which return and finally are executed, finally is definitely executed.
The problem arises again. Since finally must have been executed, the result of our first code should be 3 instead of 1?
Cause exposure
To reveal the secrets, we need to compile the code using the. NET code in the first code:
18: {00000000 55 push ebp 00000001 8B EC mov ebp,esp 00000003 57 push edi 00000004 56 push esi 00000005 53 push ebx 00000006 83 EC 38 sub esp,38h 00000009 8B F1 mov esi,ecx 0000000b 8D 7D C8 lea edi,[ebp-38h] 0000000e B9 0B 00 00 00 mov ecx,0Bh 00000013 33 C0 xor eax,eax 00000015 F3 AB rep stos dword ptr es:[edi] 00000017 8B CE mov ecx,esi 00000019 33 C0 xor eax,eax 0000001b 89 45 E4 mov dword ptr [ebp-1Ch],eax 0000001e 89 4D C4 mov dword ptr [ebp-3Ch],ecx 00000021 83 3D 10 29 DD 03 00 cmp dword ptr ds:[03DD2910h],0 00000028 74 05 je 0000002F 0000002a E8 E8 52 DE 6A call 6ADE5317 0000002f 33 D2 xor edx,edx 00000031 89 55 C0 mov dword ptr [ebp-40h],edx 00000034 33 D2 xor edx,edx 00000036 89 55 BC mov dword ptr [ebp-44h],edx 00000039 90 nop 19: Int32 i = 0;0000003a 33 D2 xor edx,edx 0000003c 89 55 C0 mov dword ptr [ebp-40h],edx 20: try 21: {0000003f 90 nop 22: i = 1;00000040 C7 45 C0 01 00 00 00 mov dword ptr [ebp-40h],1 23: return i;00000047 8B 45 C0 mov eax,dword ptr [ebp-40h] 0000004a 89 45 BC mov dword ptr [ebp-44h],eax 0000004d 90 nop 0000004e C7 45 E0 00 00 00 00 mov dword ptr [ebp-20h],0 00000055 C7 45 E4 FC 00 00 00 mov dword ptr [ebp-1Ch],0FCh 0000005c 68 8D 18 E5 03 push 3E5188Dh 00000061 EB 29 jmp 0000008C 24: } 25: catch00000063 90 nop 26: {00000064 90 nop 27: i = 2;00000065 C7 45 C0 02 00 00 00 mov dword ptr [ebp-40h],2 28: return i;0000006c 8B 45 C0 mov eax,dword ptr [ebp-40h] 0000006f 89 45 BC mov dword ptr [ebp-44h],eax 00000072 E8 61 0A B3 6A call 6AB30AD8 00000077 C7 45 E0 00 00 00 00 mov dword ptr [ebp-20h],0 0000007e C7 45 E4 FC 00 00 00 mov dword ptr [ebp-1Ch],0FCh 00000085 68 84 18 E5 03 push 3E51884h 0000008a EB 00 jmp 0000008C 29: } 30: finally 31: {0000008c 90 nop 32: i = 3;0000008d C7 45 C0 03 00 00 00 mov dword ptr [ebp-40h],3 33: }00000094 90 nop 00000095 58 pop eax 00000096 FF E0 jmp eax 00000098 90 nop 34: }00000099 8B 45 BC mov eax,dword ptr [ebp-44h] 0000009c 8D 65 F4 lea esp,[ebp-0Ch] 0000009f 5B pop ebx 000000a0 5E pop esi 000000a1 5F pop edi 000000a2 5D pop ebp 000000a3 C3 ret 000000a4 C7 45 E4 00 00 00 00 mov dword ptr [ebp-1Ch],0 000000ab EB EB jmp 00000098 000000ad C7 45 E4 00 00 00 00 mov dword ptr [ebp-1Ch],0 000000b4 EB E2 jmp 00000098
This is actually the real path of. Net execution.
1. The first one we can see is the RET command of the last 5th rows. This is the return command, that is, our return is not actually the actual method exit position.
2. Check the Il of return I ;.
00000047 8B 45 C0 mov eax,dword ptr [ebp-40h] 0000004a 89 45 BC mov dword ptr [ebp-44h],eax 0000004d 90 nop 0000004e C7 45 E0 00 00 00 00 mov dword ptr [ebp-20h],0 00000055 C7 45 E4 FC 00 00 00 mov dword ptr [ebp-1Ch],0FCh 0000005c 68 8D 18 E5 03 push 3E5188Dh 00000061 EB 29 jmp 0000008C
In addition to some mov operations, the method execution is not aborted. The last sentence is the JMP jump command, and the jump address is exactly the starting address of the Finally block, that is, finally is executed after this sentence is executed.
3. Before analyzing the return I Il, let's take a look at the two sections of the C # code with no specific correspondence at the beginning and end of the method, they are the "Opening" prologue code: Responsible for initializing the method before the method starts. The most important thing is to allocate memory for the local variables of the method on the thread stack, and allocate memory for the return value, from the code you can see that the two addresses are allocated and initialized [ebp-40h] [ebp-44h]; end code epilogue code: method to complete cleaning and return to the caller.
4, then the last piece, we can see that each operation I value, there will be mov dword ptr [ebp-40h] such an operation, that is to say, this address stores the I value. Then we can see from the return I; Il code that it first executes two operations: the value of the [ebp-40h] To eax, and then the eax value to the [ebp-44h], that is, the return value is saved in the [ebp-44h] address.
5, to the end, just put the value of the [ebp-44h] address into the data register, finally obtained by the caller.
The truth is that the variables and return values are stored in two different places. When I is returned, only the I value is used to fill in the return value address. When finally, the I value is changed again, but does not affect the return value. As for JS Finally, you can return I again; or you may have modified the value of the address that returned the value.
What about the reference type?
For the value type, the allocated address is saved directly as a value. Modifying the I value once again does not affect the return value. For the reference type, the address is saved as a pointer. Should it be another time?
function testtry2() { var o = {}; o.i = 0; try { o.i = 1; return o; } catch (e) { o.i = 2; return o; } finally { o.i = 3; } }
Boldly guess the I value of the returned object. Right, it is 3.
After the drill is completed.