enumなんとかをfor..inで、その3
ついに、これだけの記述で、Enumなんとかをfor..inで使える形にラップできるようになりました。
type TWindowEnumerator = class(TEnumerator) function GetCurrent: HWND; property Current: HWND read GetCurrent; end; TEnumWindows = record function GetEnumerator: TWindowEnumerator; end; function TWindowEnumerator.GetCurrent: HWND; begin Result := HWND(inherited GetCurrent) end; function TEnumWindows.GetEnumerator: TWindowEnumerator; begin Result := TWindowEnumerator(Utils.GetEnumerator(TWindowEnumerator)); EnumerateWindows(@Utils.Each, Result); end;
TEnumWindows.GetEnumeratorがスタックを4バイトResultのために消費しているのを前提にリターンアドレスを書き換えたりとかなり行儀悪いというか最適化切ってスタックフレーム作るようにしたらそれで動かなくなるというか。
type TEnumerator = class(TObject) private caller_ebx, caller_esi, caller_edi, caller_ebp, caller_eip: Pointer; enum_ebx, enum_esi, enum_edi, enum_esp, enum_eip: Pointer; save_esp, save_eip: Pointer; base_esp: Pointer; FNext, FCurrent: Pointer; FState: (esStart, esEnumerating, esTerminated, esEnded); public procedure BeforeDestruction; override; function MoveNext: Boolean; register; property GetCurrent: Pointer read FCurrent; end; TEnumeratorClass = class of TEnumerator; function Each(Item: Pointer; Self: TEnumerator): Boolean cdecl; function GetEnumerator(Enumerator: TEnumeratorClass): TEnumerator; register;
function Each(Item: Pointer; Self: TEnumerator): Boolean cdecl;
asm
mov edx, Item
mov eax, Self
push ebp
mov TEnumerator[eax].FNext, edx
mov TEnumerator[eax].enum_ebx, ebx
mov TEnumerator[eax].enum_esi, esi
mov TEnumerator[eax].enum_edi, edi
mov TEnumerator[eax].enum_esp, esp
mov TEnumerator[eax].enum_eip, offset @Next
mov ebx, TEnumerator[eax].caller_ebx
mov esi, TEnumerator[eax].caller_esi
mov edi, TEnumerator[eax].caller_edi
mov edx, TEnumerator[eax].caller_eip
mov ebp, TEnumerator[eax].caller_ebp
jmp edx
@Next:
pop ebp
mov eax, Self
mov al, TEnumerator[eax].FState
cmp al, esTerminated
setae al
end;
function GetEnumerator(Enumerator: TEnumeratorClass): TEnumerator; register;
asm
{インスタンス作成, eax = VMT}
mov dl, 1
call TEnumerator.Create
lea edx, [esp + 12] {for..inのesp, 親関数はスタック4バイト消費前提}
mov TEnumerator[eax].base_esp, edx
mov TEnumerator[eax].caller_ebx, ebx
mov TEnumerator[eax].caller_esi, esi
mov TEnumerator[eax].caller_edi, edi
mov TEnumerator[eax].caller_ebp, ebp
mov edx, [esp + 8] {for..inのリターンアドレスを得る}
mov TEnumerator[eax].caller_eip, edx
{親関数の後ここに戻ってくるように}
mov [esp + 8], offset @Return
ret
@Return:
{ループ中に終了して戻ってきたなら中断位置へ}
mov dl, TEnumerator[eax].FState
mov TEnumerator[eax].FState, esEnded
test dl, dl
jz @Exit
mov ebx, TEnumerator[eax].caller_ebx
mov esi, TEnumerator[eax].caller_esi
mov edi, TEnumerator[eax].caller_edi
mov ebp, TEnumerator[eax].caller_ebp
@Exit:
mov edx, TEnumerator[eax].caller_eip
jmp edx
end;
procedure TEnumerator.BeforeDestruction;
asm
mov dl, TEnumerator[eax].FState
cmp dl, esEnumerating
jnz @Exit
{終了フラグ}
mov TEnumerator[eax].FState, esTerminated
{コンテキスト保存}
pop ecx
mov TEnumerator[eax].save_eip, ecx
mov TEnumerator[eax].save_esp, esp
{スイッチ}
mov TEnumerator[eax].caller_ebx, ebx
mov TEnumerator[eax].caller_esi, esi
mov TEnumerator[eax].caller_edi, edi
mov TEnumerator[eax].caller_ebp, ebp
mov TEnumerator[eax].caller_eip, offset @Next
mov ebx, TEnumerator[eax].enum_ebx
mov esi, TEnumerator[eax].enum_esi
mov edi, TEnumerator[eax].enum_edi
mov esp, TEnumerator[eax].enum_esp
mov edx, TEnumerator[eax].enum_eip
jmp edx
@Next:
{コンテキスト復帰}
mov esp, TEnumerator[eax].save_esp
mov ecx, TEnumerator[eax].save_eip
push ecx
{スタック巻き戻し}
mov eax, TEnumerator[eax].base_esp
lea edx, [eax - 24] {BeforeDestruction, @BeforeDestruction(pop*2), Destroy, Finally}
mov eax, esp
mov ecx, 24
push edx
call System.Move
pop esp
@Exit:
end;
function TEnumerator.MoveNext: Boolean;
asm
{FCurrent := FNext}
mov edx, TEnumerator[eax].FNext
mov TEnumerator[eax].FCurrent, edx
{終了判定}
xor edx, edx
mov dl, TEnumerator[eax].FState
mov edx, dword ptr[@JumpTable + edx * 4]
jmp edx
@Start:
mov TEnumerator[eax].FState, esEnumerating
@Enumerating:
{コンテキスト保存}
pop ecx
mov TEnumerator[eax].save_esp, esp
mov TEnumerator[eax].save_eip, ecx
{スイッチ}
mov TEnumerator[eax].caller_ebx, ebx
mov TEnumerator[eax].caller_esi, esi
mov TEnumerator[eax].caller_edi, edi
mov TEnumerator[eax].caller_ebp, ebp
mov TEnumerator[eax].caller_eip, offset @Next
mov ebx, TEnumerator[eax].enum_ebx
mov esi, TEnumerator[eax].enum_esi
mov edi, TEnumerator[eax].enum_edi
mov esp, TEnumerator[eax].enum_esp
mov edx, TEnumerator[eax].enum_eip
jmp edx
@Next:
{コンテキスト復帰}
mov esp, TEnumerator[eax].save_esp
mov ecx, TEnumerator[eax].save_eip
push ecx
mov dl, TEnumerator[eax].FState
cmp dl, esEnded
jne @Continue
{スタック巻き戻し}
mov eax, TEnumerator[eax].base_esp
lea edx, [eax - 16] {リターンアドレス, 例外ハンドラpush*3}
mov eax, esp
mov ecx, 16
push edx
call System.Move
pop esp
@Continue:
mov al, 1
ret
@Terminated:
mov TEnumerator[eax].FState, esEnded
mov al, 1
ret
@Ended:
xor al, al
ret
@JumpTable:
dd @Start
dd @Enumerating
dd @Terminated
dd @Ended
end;