11using System ;
22using System . Collections . Concurrent ;
3+ using System . Collections . Generic ;
34using System . IO ;
45using System . Linq ;
56using System . Threading . Tasks ;
@@ -27,7 +28,7 @@ public static class DotNetDisassemblerCache
2728 /// <summary>
2829 /// 逆アセンブラのバージョン決定に失敗した際のメッセージ(ラベル付き)
2930 /// </summary>
30- private const string ERROR_FAILED_TO_DETERMINE_DISASSEMBLER_VERSION_FOR_LABEL = ERROR_FAILED_TO_DETERMINE_DISASSEMBLER_VERSION + " for label:" ;
31+ private const string ERROR_FAILED_TO_DETERMINE_DISASSEMBLER_VERSION_FOR_LABEL = ERROR_FAILED_TO_DETERMINE_DISASSEMBLER_VERSION + " for label: '{0}'. " ;
3132
3233 /// <summary>
3334 /// 逆アセンブラのバージョン決定に失敗した際のメッセージ(無効なラベル)
@@ -82,18 +83,21 @@ private enum DisassemblerKind
8283 /// <exception cref="InvalidOperationException">コマンドラベルが無効、またはバージョン取得に失敗した場合。</exception>
8384 public static async Task < string > GetDisassemblerVersionAsync ( string disassembleCommandWithArguments )
8485 {
86+ // 入力の妥当性をまずチェック。空なら即例外。
8587 if ( string . IsNullOrWhiteSpace ( disassembleCommandWithArguments ) )
8688 {
8789 throw new InvalidOperationException ( ERROR_FAILED_TO_DETERMINE_DISASSEMBLER_VERSION_EMPTY ) ;
8890 }
8991
92+ // コマンドから逆アセンブラ種別とキャッシュキー、実行ファイル名を抽出。
9093 var ( disassemblerKind , disassemblerVersionCacheKey , disassemblerExe ) = GetDisassemblerInfo ( disassembleCommandWithArguments ) ;
94+ // 種別に応じてバージョン取得ロジックを切り替え。該当が無ければエラーとして扱う。
9195 return disassemblerKind switch
9296 {
9397 DisassemblerKind . DotnetIldasm => await GetVersionForDotnetIldasmAsync ( disassemblerVersionCacheKey , disassemblerExe ) ,
9498 DisassemblerKind . Ildasm => await GetVersionForIldasmAsync ( disassemblerVersionCacheKey , disassemblerExe ) ,
9599 DisassemblerKind . Ilspy => await GetVersionForIlspyAsync ( disassemblerVersionCacheKey , disassemblerExe ) ,
96- _ => throw new InvalidOperationException ( $ " { ERROR_FAILED_TO_DETERMINE_DISASSEMBLER_VERSION_FOR_LABEL } ' { disassembleCommandWithArguments } '." )
100+ _ => throw new InvalidOperationException ( string . Format ( ERROR_FAILED_TO_DETERMINE_DISASSEMBLER_VERSION_FOR_LABEL , disassembleCommandWithArguments ) )
97101 } ;
98102 }
99103
@@ -102,12 +106,14 @@ public static async Task<string> GetDisassemblerVersionAsync(string disassembleC
102106 /// </summary>
103107 private static ( DisassemblerKind disassemblerKind , string disassemblerVersionCacheKey , string disassemblerExe ) GetDisassemblerInfo ( string disassembleCommandWithArguments )
104108 {
109+ // コマンド文字列をトークン化し、先頭のコマンド名やラベルを抽出する。
105110 var tokens = Utility . TokenizeCommand ( disassembleCommandWithArguments ) ;
106111 if ( tokens . Count == 0 )
107112 {
108113 throw new InvalidOperationException ( string . Format ( ERROR_FAILED_TO_DETERMINE_DISASSEMBLER_VERSION_INVALID , disassembleCommandWithArguments ) ) ;
109114 }
110115
116+ // "dotnet ildasm ..." 形式かどうかを確認。dotnet 経由の場合は muxer を実行ファイルとする。
111117 if ( string . Equals ( tokens [ 0 ] , Constants . DOTNET_MUXER , StringComparison . OrdinalIgnoreCase ) )
112118 {
113119 if ( tokens . Count >= 2 && string . Equals ( tokens [ 1 ] , Constants . DOTNET_ILDASM , StringComparison . OrdinalIgnoreCase ) )
@@ -116,6 +122,7 @@ private static (DisassemblerKind disassemblerKind, string disassemblerVersionCac
116122 }
117123 }
118124
125+ // それ以外は先頭トークンのファイル名で直接判定する。
119126 var exe = tokens [ 0 ] ;
120127 return Path . GetFileName ( exe ) ? . ToLowerInvariant ( ) switch
121128 {
@@ -130,92 +137,81 @@ private static (DisassemblerKind disassemblerKind, string disassemblerVersionCac
130137 /// </summary>
131138 private static async Task < string > GetVersionForDotnetIldasmAsync ( string disassemblerVersionCacheKey , string disassemblerExe )
132139 {
133- var disassemblerVersion = await TryGetDisassemblerVersionAsync ( disassemblerVersionCacheKey , disassemblerExe , [ Constants . DOTNET_ILDASM , FLAG_VERSION_LONG ] ) ;
134- if ( ! string . IsNullOrWhiteSpace ( disassemblerVersion ) )
140+ // dotnet-ildasmはサブコマンド付きで --version / -v を順に試す。
141+ var attempts = new ( string [ ] args , bool useFirstLine ) [ ]
135142 {
136- disassemblerVersionCache [ disassemblerVersionCacheKey ] = disassemblerVersion ;
137- return disassemblerVersion ;
138- }
139-
140- disassemblerVersion = await TryGetDisassemblerVersionAsync ( disassemblerVersionCacheKey , disassemblerExe , [ Constants . DOTNET_ILDASM , FLAG_VERSION_SHORT ] ) ;
141- if ( ! string . IsNullOrWhiteSpace ( disassemblerVersion ) )
142- {
143- disassemblerVersionCache [ disassemblerVersionCacheKey ] = disassemblerVersion ;
144- return disassemblerVersion ;
145- }
146-
147- if ( disassemblerVersionCache . TryGetValue ( disassemblerVersionCacheKey , out var cachedDisassemblerVersion ) )
148- {
149- return cachedDisassemblerVersion ;
150- }
151-
152- throw new InvalidOperationException ( $ "{ ERROR_FAILED_TO_GET_VERSION } '{ Constants . DOTNET_ILDASM } ' ({ nameof ( disassemblerVersionCacheKey ) } ='{ disassemblerVersionCacheKey } ').") ;
143+ ( [ Constants . DOTNET_ILDASM , FLAG_VERSION_LONG ] , false ) ,
144+ ( [ Constants . DOTNET_ILDASM , FLAG_VERSION_SHORT ] , false )
145+ } ;
146+ return await GetVersionWithFallbacksAsync ( disassemblerVersionCacheKey , disassemblerExe , attempts , Constants . DOTNET_ILDASM ) ;
153147 }
154148
155149 /// <summary>
156150 /// ildasm のバージョン情報を取得(キャッシュ)します。
157151 /// </summary>
158152 private static async Task < string > GetVersionForIldasmAsync ( string disassemblerVersionCacheKey , string disassemblerExe )
159153 {
160- var disassemblerVersion = await TryGetDisassemblerVersionAsync ( disassemblerVersionCacheKey , disassemblerExe , [ FLAG_VERSION_LONG ] ) ;
161- if ( ! string . IsNullOrWhiteSpace ( disassemblerVersion ) )
162- {
163- disassemblerVersionCache [ disassemblerVersionCacheKey ] = disassemblerVersion ;
164- return disassemblerVersion ;
165- }
166-
167- disassemblerVersion = await TryGetDisassemblerVersionAsync ( disassemblerVersionCacheKey , disassemblerExe , [ FLAG_VERSION_SHORT ] ) ;
168- if ( ! string . IsNullOrWhiteSpace ( disassemblerVersion ) )
169- {
170- disassemblerVersionCache [ disassemblerVersionCacheKey ] = disassemblerVersion ;
171- return disassemblerVersion ;
172- }
173-
174- if ( disassemblerVersionCache . TryGetValue ( disassemblerVersionCacheKey , out var cachedDisassemblerVersion ) )
154+ // ildasm は単純に --version と -v の二段構え。
155+ var attempts = new ( string [ ] args , bool useFirstLine ) [ ]
175156 {
176- return cachedDisassemblerVersion ;
177- }
178-
179- throw new InvalidOperationException ( $ " { ERROR_FAILED_TO_GET_VERSION } ' { Constants . DOTNET_ILDASM } ' ( { nameof ( disassemblerVersionCacheKey ) } =' { disassemblerVersionCacheKey } ')." ) ;
157+ ( [ FLAG_VERSION_LONG ] , false ) ,
158+ ( [ FLAG_VERSION_SHORT ] , false )
159+ } ;
160+ return await GetVersionWithFallbacksAsync ( disassemblerVersionCacheKey , disassemblerExe , attempts , Constants . DOTNET_ILDASM ) ;
180161 }
181162
182163 /// <summary>
183164 /// ilspycmd のバージョン文字列を取得してキャッシュします(--version/-v/-h の順に試行)。
184165 /// </summary>
185166 private static async Task < string > GetVersionForIlspyAsync ( string disassemblerVersionCacheKey , string disassemblerExe )
186167 {
187- // まずは --version で取得を試みる(成功すればそのままキャッシュ)。
188- var disassemblerVersion = await TryGetDisassemblerVersionAsync ( disassemblerVersionCacheKey , disassemblerExe , [ FLAG_VERSION_LONG ] ) ;
189- if ( ! string . IsNullOrWhiteSpace ( disassemblerVersion ) )
168+ // ilspycmd は --version / -v が失敗する環境向けに -h の先頭行をフォールバックとして使用。
169+ var attempts = new ( string [ ] args , bool useFirstLine ) [ ]
190170 {
191- disassemblerVersionCache [ disassemblerVersionCacheKey ] = disassemblerVersion ;
192- return disassemblerVersion ;
193- }
171+ ( [ FLAG_VERSION_LONG ] , false ) ,
172+ ( [ FLAG_VERSION_SHORT ] , false ) ,
173+ ( [ FLAG_HELP_SHORT ] , true )
174+ } ;
175+ return await GetVersionWithFallbacksAsync ( disassemblerVersionCacheKey , disassemblerExe , attempts , Constants . ILSPY_CMD ) ;
176+ }
194177
195- // 続いて -v(短縮記法)を試す。
196- disassemblerVersion = await TryGetDisassemblerVersionAsync ( disassemblerVersionCacheKey , disassemblerExe , [ FLAG_VERSION_SHORT ] ) ;
197- if ( ! string . IsNullOrWhiteSpace ( disassemblerVersion ) )
178+ /// <summary>
179+ /// 複数パターンの引数でバージョン取得を試み、成功した結果をキャッシュおよび返却するユーティリティ。
180+ /// </summary>
181+ private static async Task < string > GetVersionWithFallbacksAsync (
182+ string disassemblerVersionCacheKey ,
183+ string disassemblerExe ,
184+ IEnumerable < ( string [ ] args , bool useFirstLine ) > attempts ,
185+ string toolName )
186+ {
187+ // 指定された順にバージョン取得の試行を行う。成功した時点で結果を返し、キャッシュにも保存。
188+ foreach ( var ( args , useFirstLine ) in attempts )
198189 {
199- disassemblerVersionCache [ disassemblerVersionCacheKey ] = disassemblerVersion ;
200- return disassemblerVersion ;
201- }
190+ var rawVersion = await TryGetDisassemblerVersionAsync ( disassemblerVersionCacheKey , disassemblerExe , args ) ;
191+ if ( string . IsNullOrWhiteSpace ( rawVersion ) )
192+ {
193+ continue ;
194+ }
202195
203- // --version/-v が失敗するケースでは -h 出力の1行目にバージョンが含まれるため fallback する。
204- disassemblerVersion = await TryGetDisassemblerVersionAsync ( disassemblerVersionCacheKey , disassemblerExe , [ FLAG_HELP_SHORT ] ) ;
205- if ( ! string . IsNullOrWhiteSpace ( disassemblerVersion ) )
206- {
207- var disassemblerVersionfirstLine = disassemblerVersion . Split ( '\n ' ) . FirstOrDefault ( ) ? . Trim ( ) ;
208- disassemblerVersionCache [ disassemblerVersionCacheKey ] = disassemblerVersionfirstLine ;
209- return disassemblerVersionfirstLine ;
196+ var processedVersion = useFirstLine
197+ ? rawVersion . Split ( '\n ' ) . FirstOrDefault ( ) ? . Trim ( )
198+ : rawVersion ;
199+
200+ if ( ! string . IsNullOrWhiteSpace ( processedVersion ) )
201+ {
202+ disassemblerVersionCache [ disassemblerVersionCacheKey ] = processedVersion ;
203+ return processedVersion ;
204+ }
210205 }
211206
207+ // すべて失敗した場合でも、過去に成功した結果が残っていればそちらを返す。
212208 if ( disassemblerVersionCache . TryGetValue ( disassemblerVersionCacheKey , out var cachedDisassemblerVersion ) )
213209 {
214210 return cachedDisassemblerVersion ;
215211 }
216212
217- // いずれの方法でも取得できない場合はエラーとして扱う 。
218- throw new InvalidOperationException ( $ "{ ERROR_FAILED_TO_GET_VERSION } '{ Constants . ILSPY_CMD } ' ({ nameof ( disassemblerVersionCacheKey ) } ='{ disassemblerVersionCacheKey } ').") ;
213+ // キャッシュにも存在しない場合は例外を投げて呼び出し元へ通知 。
214+ throw new InvalidOperationException ( $ "{ ERROR_FAILED_TO_GET_VERSION } '{ toolName } ' ({ nameof ( disassemblerVersionCacheKey ) } ='{ disassemblerVersionCacheKey } ').") ;
219215 }
220216
221217 /// <summary>
0 commit comments