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;