MOV EAX,[ESP+4] // EAX 是 TExceptionRecord 的指针 TEST [EAX].TExceptionRecord.ExceptionFlags,cUnwindInProgress JNE @@exit // ZF = 0 (结果是 !0 )
// 如果是 Delphi Exception,跳至 Delphi Exception 处理过程 CMP [EAX].TExceptionRecord.ExceptionCode,cDelphiException JE @@DelphiException
CLD
CALL _FpuInit // 重置浮点数处理器 (猜想)
// 映射操作系统异常至 Delphi 异常类
MOV EDX,ExceptClsProc // (ExceptClsProc 指向 GetExceptionClass) TEST EDX,EDX // 如果 ExceptClsProc 为 nil 则退出 JE @@exit CALL EDX
TEST EAX,EAX // 检查是否找到相应的 ExceptionClass JNE @@common // ExceptionClass 映射成功,跳到 common JMP @@exit // 映射不成功,退出
// 通过 TExceptionRecord 结构获得 Exception 对象指针 @@DelphiException:
MOV EAX,[EAX].TExceptionRecord.ExceptObject
MOV EAX,[EAX] { load vtable of exception object }
// (EAX 是当前 Exception 对象的 VMT ptr) @@common:
MOV EDX,[ESP+8] // EDX <- TExcFrame 的地址
// 保存 4 个寄存器 PUSH EBX PUSH ESI PUSH EDI PUSH EBP
MOV ECX,[EDX].TExcFrame.desc // ECX <- TExcFrame.desc 地址 MOV EBX,[ECX].TExcDesc.cnt // EBX <- 异常描述个数
LEA ESI,[ECX].TExcDesc.excTab { point ECX to exc descriptor table } // ESI <- 异常描述的地址
MOV EBP,EAX { load vtable of exception object } // EBP <- 当前异常对象的 VMT ptr
// 匹配当前异常和异常描述表中的异常(并递归匹配父类) @@innerLoop:
MOV EAX,[ESI].TExcDescEntry.vTable
// EAX <- 异常描述表中的 VMT 指针的 指针(注意,二级指针) TEST EAX,EAX { catch all clause? } // 是 0 则处理所有异常
JE @@doHandler { yes: go execute handler }
MOV EDI,EBP { load vtable of exception object } JMP @@haveVMT
@@vtLoop:
MOV EDI,[EDI] // EDX <- 当前异常对象的 VMT @@haveVMT:
MOV EAX,[EAX] // EAX <- 异常描述表中的 VMT
CMP EAX,EDI // 如果 VMT 相同,跳到 @@doHandler JE @@doHandler
// 为什么要比较以下项?
MOV ECX,[EAX].vmtInstanceSize // 比较对象实例大小,如果不同 CMP ECX,[EDI].vmtInstanceSize // 则跳到 @@parent JNE @@parent
MOV EAX,[EAX].vmtClassName // 比较 ClassName,如果不同 MOV EDX,[EDI].vmtClassName // 则跳到 @@parent,相同则 // 跳至 @@doHandler XOR ECX,ECX MOV CL,[EAX] CMP CL,[EDX] JNE @@parent
INC EAX INC EDX
CALL _AStrCmp JE @@doHandler
@@parent:
MOV EDI,[EDI].vmtParent { load vtable of parent } MOV EAX,[ESI].TExcDescEntry.vTable // 获取当前异常的父类 VTM TEST EDI,EDI // 如果父类 VMT 不等于 0, JNE @@vtLoop // 则重新比较 VMT 是否相同
ADD ESI,8 // 步进到异常描述表的一下项 DEC EBX // 减少计数
JNZ @@innerLoop // 重新进行异常匹配
POP EBP // 恢复堆栈,退出 POP EDI POP ESI POP EBX JMP @@exit
@@doHandler:
MOV EAX,[ESP+4+4*4] // (4*4 是上面 PUSH 的四个寄存器) // 如果是 Delphi 异常,则从 TExceptionRecord 结构中取得异常对象 CMP [EAX].TExceptionRecord.ExceptionCode,cDelphiException MOV EDX,[EAX].TExceptionRecord.ExceptObject MOV ECX,[EAX].TExceptionRecord.ExceptAddr JE @@haveObject
// 否则通过 ExceptObjProc 取得异常对象 CALL ExceptObjProc
// 以下是 Delphi IDE 调试的集成处理 (未阅)
MOV EDX,[ESP+12+4*4] // EDX <- CONTEXT ptr
CALL NotifyNonDelphiException // 向 Delphi Debuger 发送异常通知 CMP BYTE PTR JITEnable,0 JBE @@NoJIT
CMP BYTE PTR DebugHook,0
JA @@noJIT { Do not JIT if debugging }
// 调用 UnhandledExceptionFilter API LEA ECX,[ESP+4]
PUSH EAX // 保存当前 EAX 值 PUSH ECX
CALL UnhandledExceptionFilter
CMP EAX,EXCEPTION_CONTINUE_SEARCH
POP EAX // 恢复当前 EAX 值 JE @@exit
@@noJIT:
MOV EDX,EAX // EDX <- 异常对象指针 MOV EAX,[ESP+4+4*4] // ECX <- 异常发生时的地址 MOV ECX,[EAX].TExceptionRecord.ExceptionAddress JMP @@GoUnwind
@@haveObject:
// 有 Delphi Exception 对象时进行的 IDE Debug 设置 CMP BYTE PTR JITEnable,1 JBE @@GoUnwind
CMP BYTE PTR DebugHook,0 JA @@GoUnwind PUSH EAX
LEA EAX,[ESP+8] PUSH EDX PUSH ECX PUSH EAX
CALL UnhandledExceptionFilter
CMP EAX,EXCEPTION_CONTINUE_SEARCH POP ECX POP EDX POP EAX JE @@exit
@@GoUnwind:
XOR EBX,EBX
MOV EBX,FS:[EBX]
PUSH EBX { Save topmost frame } PUSH EAX { Save exception record } PUSH EDX { Save exception object } PUSH ECX { Save exception address }
MOV EDX,[ESP+8+8*4]
OR [EAX].TExceptionRecord.ExceptionFlags,cUnwinding
PUSH ESI { Save handler entry }
// ESI <- TExcDescEntry.vTable 指针 // 用于下段 EBX 获得 handler
PUSH 0 // 可能是参数个数
PUSH EAX // EAX <- TExceptionRecord 指针 PUSH offset @@returnAddress // 可能是 RtlUnwind 返回地址 PUSH EDX // EDX <- TExcFrame 指针 CALL RtlUnwindProc // 调用 RtlUnwind API
@@returnAddress:
POP EBX { Restore handler entry }
MOV EDI,[ESP+8+8*4] // EDI <- TExcFrame 指针
{ Make the RaiseList entry on the stack }
CALL SysInit.@GetTLS // 在堆栈中构造 RaiseList PUSH [EAX].RaiseListPtr
MOV [EAX].RaiseListPtr,ESP
MOV EBP,[EDI].TExcFrame.hEBP // 恢复 try 语句时的 EBP MOV [EDI].TExcFrame.desc,offset @@exceptFinally
// 设置异常处理地址?? // 向 IDE 发通知
MOV EAX,[ESP].TRaiseFrame.ExceptObject CALL NotifyOnExcept
JMP [EBX].TExcDescEntry.handler // 调用异常处理过程
@@exceptFinally:
JMP _HandleFinally
@@destroyExcept:
{ we come here if an exception handler has thrown yet another exception } { we need to destroy the exception object and pop the raise list. }
CALL SysInit.@GetTLS
MOV ECX,[EAX].RaiseListPtr
MOV EDX,[ECX].TRaiseFrame.NextRaise MOV [EAX].RaiseListPtr,EDX
MOV EAX,[ECX].TRaiseFrame.ExceptObject JMP TObject.Free @@exit:
MOV EAX,1 end; {
The following NotifyXXXX routines are used to \ as a signaling mechanism to an interested debugger. If the debugger sets the DebugHook flag to 1 or 2, then all exception processing is tracked by raising these special exceptions. The debugger *MUST* respond to the debug event with DBG_CONTINUE so that normal processing will occur. }
DebugHook: Byte platform = 0; { 1 to notify debugger of non-Delphi exceptions
>1 to notify debugger of exception unwinding } JITEnable: Byte platform = 0; { 1 to call UnhandledExceptionFilter if the exception
is not a Pascal exception.
>1 to call UnhandledExceptionFilter for all exceptions }
function MapException(P: PExceptionRecord): TRuntimeError; begin
case P.ExceptionCode of
STATUS_INTEGER_DIVIDE_BY_ZERO: Result := System.reDivByZero; STATUS_ARRAY_BOUNDS_EXCEEDED: Result := System.reRangeError; STATUS_INTEGER_OVERFLOW:
Result := System.reIntOverflow; STATUS_FLOAT_INEXACT_RESULT, STATUS_FLOAT_INVALID_OPERATION,
STATUS_FLOAT_STACK_CHECK:
Result := System.reInvalidOp; STATUS_FLOAT_DIVIDE_BY_ZERO:
Result := System.reZeroDivide; STATUS_FLOAT_OVERFLOW:
Result := System.reOverflow; STATUS_FLOAT_UNDERFLOW,
STATUS_FLOAT_DENORMAL_OPERAND: Result := System.reUnderflow; STATUS_ACCESS_VIOLATION:
Result := System.reAccessViolation; STATUS_PRIVILEGED_INSTRUCTION:
Result := System.rePrivInstruction; STATUS_CONTROL_C_EXIT:
Result := System.reControlBreak; STATUS_STACK_OVERFLOW:
Result := System.reStackOverflow; else
Result := System.reExternalException; end; end;
// 注意前三个异常类型没有异常数组定义
TRuntimeError = (reNone, reOutOfMemory, reInvalidPtr, reDivByZero, reRangeError, reIntOverflow, reInvalidOp, reZeroDivide, reOverflow, reUnderflow, reInvalidCast, reAccessViolation, rePrivInstruction, reControlBreak, reStackOverflow, { reVar* used in Variants.pas } reVarTypeCast, reVarInvalidOp,
reVarDispatch, reVarArrayCreate, reVarNotArray, reVarArrayBounds, reAssertionFailed,
reExternalException, { not used here; in SysUtils } reIntfCastError, reSafeCallError );
{$NODEFINE TRuntimeError}
const
ExceptMap: array[Ord(reDivByZero)..Ord(High(TRuntimeError))] of TExceptRec = ( (EClass: EDivByZero; EIdent: SDivByZero), (EClass: ERangeError; EIdent: SRangeError), (EClass: EIntOverflow; EIdent: SIntOverflow), (EClass: EInvalidOp; EIdent: SInvalidOp), (EClass: EZeroDivide; EIdent: SZeroDivide), (EClass: EOverflow; EIdent: SOverflow), (EClass: EUnderflow; EIdent: SUnderflow), (EClass: EInvalidCast; EIdent: SInvalidCast),
(EClass: EAccessViolation; EIdent: SAccessViolationNoArg), (EClass: EPrivilege; EIdent: SPrivilege), (EClass: EControlC; EIdent: SControlC),
(EClass: EStackOverflow; EIdent: SStackOverflow), (EClass: EVariantError; EIdent: SInvalidVarCast), (EClass: EVariantError; EIdent: SInvalidVarOp), (EClass: EVariantError; EIdent: SDispatchError), (EClass: EVariantError; EIdent: SVarArrayCreate), (EClass: EVariantError; EIdent: SVarInvalid), (EClass: EVariantError; EIdent: SVarArrayBounds),
(EClass: EAssertionFailed; EIdent: SAssertionFailed),
(EClass: EExternalException; EIdent: SExternalException), (EClass: EIntfCastError; EIdent: SIntfCastError),