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;