powershell(ver1.0) で try-catch

2.0 は実装されたらしい。
でもなんか 1.0 使うらしい。

ネタ元
http://csharper.blog57.fc2.com/blog-entry-177.html
必要に迫られて catch の複数対応。
つかんだ例外の $Error は削除してる。

function CatchStatements ( $catchBlocks, $ex, [ref]$isCatched ) {
  for( $i = 0; $i -lt $catchBlocks.Count; $i++ ) {
    $targetExceptionType = $catchBlocks[$i][0];
    $catchBlock = $catchBlocks[$i][1];
    if (($() -eq $targetExceptionType) -or ( $targetExceptionType.IsAssignableFrom($ex.GetType())) ) {
      &$catchBlock $ex;
      $Error.RemoveAt($Error.Count-1);
      $isCatched.Value = $true;
      return;
    }
  }
  $isCatched.Value = $false;
  return;
}

function global:try {
  $currentArgIndex = 0;
  $tryBlock = $args[$currentArgIndex];
  $currentArgIndex++;
  $catchBlocks = New-Object Collections.ArrayList;

  if ($tryBlock -isnot [Management.Automation.ScriptBlock]) {
    throw New-Object "ArgumentException" @("try ブロックの指定が不正です。");
  }

  for( $i = $currentArgIndex; $i -lt $args.Length; $i++ ) { 
    if ("catch" -eq $args[$currentArgIndex]) {
      $currentArgIndex++;
      if ($args[$currentArgIndex] -is [Type]) {
        $targetExceptionType = $args[$currentArgIndex];
        $currentArgIndex++;
      } else {
        $targetExceptionType = [Object]
      }

      $catchBlock = $args[$currentArgIndex];
      $currentArgIndex++;

      if ($catchBlock -isnot [Management.Automation.ScriptBlock]) {
        throw New-Object "ArgumentException" @("catch ブロックの指定が不正です。");
      }
      $null = $catchBlocks.Add( @( $targetExceptionType, $catchBlock ) );
    } elseif ("finally" -eq $args[$currentArgIndex]) {
      $currentArgIndex++;
      $finallyBlock = $args[$currentArgIndex];
      $currentArgIndex++;
      if ($finallyBlock -isnot [Management.Automation.ScriptBlock]) {
        throw New-Object "ArgumentException" @("finally ブロックの指定が不正です。");
      }
      break;
    }
  }

  if (($() -eq $catchBlock) -and ($() -eq $finallyBlock)) {
    throw New-Object "ArgumentException" @("catch ブロックまたは finally ブロックを指定してください。");
  }

  & {
    $requireFinally = ($() -ne $finallyBlock);
    & {
      &$tryBlock;
      trap {
        if ($() -eq $catchBlock) {
          break;
        }

        $ex = $_.Exception;
        $isCatched = $false;
        CatchStatements $catchBlocks $ex ([ref]$isCatched);

        if( !$isCatched ) {
          break;
        } else {
          continue;
        }
      }
    };

    if ($requireFinally) {
      $requireFinally = $False;
      &$finallyBlock;
    }

    trap {
      if ($requireFinally) {
        $requireFinally = $False;
        &$finallyBlock;
      }
      break;
    }
  };
}
try {
  throw New-Object ArgumentException;  
  throw New-Object ApplicationException;  
  throw New-Object Exception;  
} catch([ArgumentException]) {
  "ArgumentException";
} catch([ApplicationException]) {
  "ApplicationException";
} catch {
  "others";
} finally {
  "finally block";
}

問題となるパターン

function TestA() {
  try {
    # なんか処理
    "process 1";
    throw New-Object Exception;
  } catch {
    "error";
    return;
  }
  "process 2";
}

TestA;

出力

process 1
error
process 2

return したところで function の return まではできないってこと。
もともとない言語仕様を無理に拡張するやりかただし。
「わかって」使えば問題ない。知らないと罠にはまるわけだが。
でも他にもワナがありそうな…!?