Skip to content

Commit f2445aa

Browse files
Merge pull request #5 from JetBrains/bug-fixes
Improve disassembly reliability and error messages
2 parents 2c43843 + 3a93649 commit f2445aa

File tree

20 files changed

+275
-65
lines changed

20 files changed

+275
-65
lines changed

CHANGELOG.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
88
## 0.1.1
99

1010
### Fixed
11-
- Fixed statistics collection
12-
- Updated plugin description
11+
- Minor fixes and improvements
12+
- Improved plugin description
1313

1414
## 0.1.0
1515

protocol/src/main/kotlin/model/rider/AsmViewerModel.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ import com.jetbrains.rider.model.nova.ide.SolutionModel
77
@Suppress("unused")
88
class AsmViewerModel : Ext(SolutionModel.Solution) {
99

10+
private val CompilerType = enum {
11+
+"Clrjit"
12+
+"Crossgen2"
13+
+"Ilc"
14+
}
15+
1016
private val ErrorCode = enum {
1117
// PSI/Navigation errors
1218
+"SourceFileNotFound"
@@ -29,6 +35,7 @@ class AsmViewerModel : Ext(SolutionModel.Solution) {
2935
+"ProjectPathNotFound"
3036
+"DotnetBuildFailed"
3137
+"DotnetPublishFailed"
38+
+"EmptyDisassembly"
3239

3340
// Runtime/Path errors
3441
+"DotNetCliNotFound"
@@ -56,7 +63,7 @@ class AsmViewerModel : Ext(SolutionModel.Solution) {
5663
field("useDotnetPublishForReload", bool)
5764
field("useDotnetBuildForReload", bool)
5865
field("targetFrameworkOverride", string.nullable)
59-
field("selectedCustomJit", string.nullable)
66+
field("selectedCompiler", CompilerType)
6067
field("disassemblyTimeoutSeconds", int)
6168
}
6269

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
using NUnit.Framework;
2+
3+
namespace ReSharperPlugin.DotNetDisassembler.Tests;
4+
5+
[TestFixture]
6+
public class FusValidationPatternsTests
7+
{
8+
[TestFixture]
9+
public class SdkTypeTests
10+
{
11+
[TestCase("Microsoft.NET.Sdk", Description = "Base SDK")]
12+
[TestCase("Microsoft.NET.Sdk.Web", Description = "Web SDK")]
13+
[TestCase("Microsoft.NET.Sdk.Worker", Description = "Worker SDK")]
14+
[TestCase("Microsoft.NET.Sdk.Razor", Description = "Razor SDK")]
15+
[TestCase("Microsoft.NET.Sdk.BlazorWebAssembly", Description = "Blazor WebAssembly SDK")]
16+
[TestCase("Microsoft.NET.Sdk.WindowsDesktop", Description = "Windows Desktop SDK (.NET Core 3.x)")]
17+
public void ShouldMatchValidSdkTypes(string sdkType)
18+
{
19+
var match = FusValidationPatterns.SdkTypeRegex.Match(sdkType);
20+
21+
Assert.That(match.Success, Is.True, $"Should match SDK type: {sdkType}");
22+
Assert.That(match.Value, Is.EqualTo(sdkType), "Should match the entire string");
23+
}
24+
25+
[TestCase("Microsoft.NET", Description = "Incomplete SDK name")]
26+
[TestCase("Microsoft.NET.Sdk.123Number", Description = "SDK extension starting with number")]
27+
[TestCase("MyCustom.NET.Sdk", Description = "Non-Microsoft SDK")]
28+
[TestCase("microsoft.net.sdk", Description = "Lowercase")]
29+
[TestCase("", Description = "Empty string")]
30+
public void ShouldNotMatchInvalidSdkTypes(string sdkType)
31+
{
32+
var match = FusValidationPatterns.SdkTypeRegex.Match(sdkType);
33+
34+
Assert.That(match.Success && match.Value == sdkType, Is.False,
35+
$"Should not match invalid SDK type: {sdkType}");
36+
}
37+
}
38+
39+
[TestFixture]
40+
public class TargetFrameworkTests
41+
{
42+
[TestCase("net8.0", Description = ".NET 8")]
43+
[TestCase("net9.0", Description = ".NET 9")]
44+
[TestCase("net6.0", Description = ".NET 6")]
45+
[TestCase("net5.0", Description = ".NET 5")]
46+
[TestCase("net8.0-windows", Description = ".NET 8 for Windows")]
47+
[TestCase("net6.0-android", Description = ".NET 6 for Android")]
48+
[TestCase("net7.0-ios", Description = ".NET 7 for iOS")]
49+
[TestCase("netcoreapp3.1", Description = ".NET Core 3.1")]
50+
[TestCase("netcoreapp2.1", Description = ".NET Core 2.1")]
51+
[TestCase("netcoreapp1.0", Description = ".NET Core 1.0")]
52+
[TestCase("netstandard2.1", Description = ".NET Standard 2.1")]
53+
[TestCase("netstandard2.0", Description = ".NET Standard 2.0")]
54+
[TestCase("netstandard1.6", Description = ".NET Standard 1.6")]
55+
[TestCase("netstandard1.0", Description = ".NET Standard 1.0")]
56+
[TestCase("net472", Description = ".NET Framework 4.7.2")]
57+
[TestCase("net48", Description = ".NET Framework 4.8")]
58+
[TestCase("net481", Description = ".NET Framework 4.8.1")]
59+
[TestCase("net462", Description = ".NET Framework 4.6.2")]
60+
public void ShouldMatchValidTargetFrameworks(string tfm)
61+
{
62+
var match = FusValidationPatterns.TargetFrameworkRegex.Match(tfm);
63+
64+
Assert.That(match.Success, Is.True, $"Should match TFM: {tfm}");
65+
Assert.That(match.Value, Is.EqualTo(tfm), "Should match the entire string");
66+
}
67+
68+
[TestCase("netframework4.8", Description = "Invalid format")]
69+
[TestCase("dotnet8.0", Description = "Wrong prefix")]
70+
[TestCase("NET8.0", Description = "Uppercase")]
71+
[TestCase("net8", Description = "Missing minor version")]
72+
[TestCase("", Description = "Empty string")]
73+
[TestCase("net8.0.0", Description = "Too many version components")]
74+
public void ShouldNotMatchInvalidTargetFrameworks(string tfm)
75+
{
76+
var match = FusValidationPatterns.TargetFrameworkRegex.Match(tfm);
77+
78+
Assert.That(match.Success && match.Value == tfm, Is.False,
79+
$"Should not match invalid TFM: {tfm}");
80+
}
81+
82+
[TestCase("net8.0-windows10.0.19041", Description = "Windows with build number")]
83+
[TestCase("net6.0-android31.0", Description = "Android with API level")]
84+
[TestCase("net8.0-ios17.2", Description = "iOS with version (from official docs)")]
85+
[TestCase("net8.0-android34.0", Description = "Android API level 34 (from official docs)")]
86+
[TestCase("net6.0-ios15.0", Description = "iOS with default version")]
87+
[TestCase("net8.0-maccatalyst", Description = "Mac Catalyst")]
88+
[TestCase("net8.0-tvos", Description = "tvOS")]
89+
public void ShouldMatchTargetFrameworksWithExtendedPlatformInfo(string tfm)
90+
{
91+
var match = FusValidationPatterns.TargetFrameworkRegex.Match(tfm);
92+
93+
Assert.That(match.Success, Is.True, $"Should match TFM with platform info: {tfm}");
94+
Assert.That(match.Value, Is.EqualTo(tfm), "Should match the entire string");
95+
}
96+
}
97+
}

src/dotnet/ReSharperPlugin.DotNetDisassembler.Tests/JitCodegenProviderTests.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ public async Task GetJitCodegen_WhenConfigurationInvalid_ShouldReturnError()
9696

9797
var config = new JitDisasmConfiguration
9898
{
99-
SelectedCustomJit = JitDisasmConfiguration.Crossgen,
99+
SelectedCustomJit = JitCompilerTypes.Crossgen,
100100
UsePgo = true
101101
};
102102

@@ -158,8 +158,8 @@ public async Task GetJitCodegen_WhenCustomRuntimeWithOldFramework_ShouldReturnEr
158158
}
159159

160160
[Test]
161-
[TestCase(JitDisasmConfiguration.Crossgen, true, AsmViewerErrorCode.RunModeNotSupportedForAot)]
162-
[TestCase(JitDisasmConfiguration.Ilc, true, AsmViewerErrorCode.RunModeNotSupportedForAot)]
161+
[TestCase(JitCompilerTypes.Crossgen, true, AsmViewerErrorCode.RunModeNotSupportedForAot)]
162+
[TestCase(JitCompilerTypes.Ilc, true, AsmViewerErrorCode.RunModeNotSupportedForAot)]
163163
public async Task GetJitCodegen_WithRunAppMode_ShouldReturnError(
164164
string customJit, bool runAppMode, AsmViewerErrorCode expectedError)
165165
{
@@ -181,8 +181,8 @@ public async Task GetJitCodegen_WithRunAppMode_ShouldReturnError(
181181
}
182182

183183
[Test]
184-
[TestCase(JitDisasmConfiguration.Crossgen, true, AsmViewerErrorCode.TieredJitNotSupportedForAot)]
185-
[TestCase(JitDisasmConfiguration.Ilc, true, AsmViewerErrorCode.TieredJitNotSupportedForAot)]
184+
[TestCase(JitCompilerTypes.Crossgen, true, AsmViewerErrorCode.TieredJitNotSupportedForAot)]
185+
[TestCase(JitCompilerTypes.Ilc, true, AsmViewerErrorCode.TieredJitNotSupportedForAot)]
186186
public async Task GetJitCodegen_WithTieredJit_ShouldReturnError(
187187
string customJit, bool useTieredJit, AsmViewerErrorCode expectedError)
188188
{
@@ -211,7 +211,7 @@ public async Task GetJitCodegen_WithFlowgraphsAndAot_ShouldReturnError()
211211

212212
var config = new JitDisasmConfiguration
213213
{
214-
SelectedCustomJit = JitDisasmConfiguration.Crossgen,
214+
SelectedCustomJit = JitCompilerTypes.Crossgen,
215215
FgEnable = true
216216
};
217217

src/dotnet/ReSharperPlugin.DotNetDisassembler/AsmCompilationService.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ public async Task<Result<string, Error>> CompileAsync(
3636
if (!result.Succeed || result.Value == null)
3737
return Result.FailWithValue(result.FailValue);
3838

39+
if (string.IsNullOrWhiteSpace(result.Value.Result))
40+
return Result.FailWithValue(new Error(AsmViewerErrorCode.EmptyDisassembly));
41+
3942
return Result.Success(result.Value.Result);
4043
}
4144
}

src/dotnet/ReSharperPlugin.DotNetDisassembler/AsmViewerErrors.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ public enum AsmViewerErrorCode
3333
ProjectPathNotFound,
3434
DotnetBuildFailed,
3535
DotnetPublishFailed,
36+
EmptyDisassembly,
3637

3738
// Runtime/Path errors
3839
DotNetCliNotFound,

src/dotnet/ReSharperPlugin.DotNetDisassembler/AsmViewerHost.cs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -134,12 +134,12 @@ private void SubscribeToTextControlChanges(Lifetime lifetime, ITextControlManage
134134
return;
135135
}
136136

137-
// IL Viewer and similar tools open read-only editors with synchronized carets.
137+
// IL Viewer, ASM Viewer and similar tools open read-only editors with synchronized carets.
138138
// We skip these and keep tracking the source editor - caret sync ensures our subscription still works.
139139
var projectFile = _documentManager.TryGetProjectFile(textControl.Document);
140-
if (projectFile != null && _fileLocationsBlacklist.Contains(projectFile.Location))
140+
if (projectFile == null || _fileLocationsBlacklist.Contains(projectFile.Location))
141141
{
142-
_logger.Verbose("Ignoring blacklisted file location: {0}", textControl.Document.Moniker);
142+
_logger.Verbose("Ignoring text control (virtual document or blacklisted): {0}", textControl.Document.Moniker);
143143
return;
144144
}
145145

@@ -174,9 +174,10 @@ private async Task RequestCompilationAsync(SequentialLifetimes compilationLifeti
174174
}
175175

176176
var textControl = _currentTextControl;
177-
if (textControl == null)
177+
if (textControl == null || !textControl.Lifetime.IsAlive)
178178
{
179-
_logger.Verbose("No text control, skipping");
179+
_logger.Verbose("No text control or text control is no longer alive, clearing UI");
180+
_model.SendResult(new CompilationResult(null, new ErrorInfo(ErrorCode.SourceFileNotFound, null)));
180181
return;
181182
}
182183

@@ -191,6 +192,12 @@ private async Task RequestCompilationAsync(SequentialLifetimes compilationLifeti
191192
return;
192193
}
193194

195+
if (!textControl.Lifetime.IsAlive)
196+
{
197+
_logger.Verbose("Text control closed during compilation, discarding result");
198+
return;
199+
}
200+
194201
if (!result.Succeed)
195202
{
196203
_logger.Warn("Compilation failed: {0} - {1}", result.FailValue.Code, result.FailValue.Details);

src/dotnet/ReSharperPlugin.DotNetDisassembler/AsmViewerModel.Generated.cs

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ internal AsmViewerModel (
112112

113113

114114

115-
protected override long SerializationHash => -8113457424028594267L;
115+
protected override long SerializationHash => -8744739113612796705L;
116116

117117
protected override Action<ISerializers> Register => RegisterDeclaredTypesSerializers;
118118
public static void RegisterDeclaredTypesSerializers(ISerializers serializers)
@@ -161,7 +161,7 @@ public static AsmViewerModel GetAsmViewerModel(this Solution solution)
161161

162162

163163
/// <summary>
164-
/// <p>Generated from: AsmViewerModel.kt:63</p>
164+
/// <p>Generated from: AsmViewerModel.kt:70</p>
165165
/// </summary>
166166
public sealed class CompilationResult : IPrintable, IEquatable<CompilationResult>
167167
{
@@ -258,6 +258,16 @@ public override string ToString()
258258
/// <summary>
259259
/// <p>Generated from: AsmViewerModel.kt:10</p>
260260
/// </summary>
261+
public enum CompilerType {
262+
Clrjit,
263+
Crossgen2,
264+
Ilc
265+
}
266+
267+
268+
/// <summary>
269+
/// <p>Generated from: AsmViewerModel.kt:16</p>
270+
/// </summary>
261271
public enum ErrorCode {
262272
SourceFileNotFound,
263273
PsiSourceFileUnavailable,
@@ -275,6 +285,7 @@ public enum ErrorCode {
275285
ProjectPathNotFound,
276286
DotnetBuildFailed,
277287
DotnetPublishFailed,
288+
EmptyDisassembly,
278289
DotNetCliNotFound,
279290
RuntimePackNotFound,
280291
CoreClrCheckedNotFound,
@@ -285,7 +296,7 @@ public enum ErrorCode {
285296

286297

287298
/// <summary>
288-
/// <p>Generated from: AsmViewerModel.kt:44</p>
299+
/// <p>Generated from: AsmViewerModel.kt:51</p>
289300
/// </summary>
290301
public sealed class ErrorInfo : IPrintable, IEquatable<ErrorInfo>
291302
{
@@ -378,7 +389,7 @@ public override string ToString()
378389

379390

380391
/// <summary>
381-
/// <p>Generated from: AsmViewerModel.kt:49</p>
392+
/// <p>Generated from: AsmViewerModel.kt:56</p>
382393
/// </summary>
383394
public sealed class JitConfiguration : IPrintable, IEquatable<JitConfiguration>
384395
{
@@ -393,7 +404,7 @@ public sealed class JitConfiguration : IPrintable, IEquatable<JitConfiguration>
393404
public bool UseDotnetPublishForReload {get; private set;}
394405
public bool UseDotnetBuildForReload {get; private set;}
395406
[CanBeNull] public string TargetFrameworkOverride {get; private set;}
396-
[CanBeNull] public string SelectedCustomJit {get; private set;}
407+
public CompilerType SelectedCompiler {get; private set;}
397408
public int DisassemblyTimeoutSeconds {get; private set;}
398409

399410
//private fields
@@ -408,7 +419,7 @@ public JitConfiguration(
408419
bool useDotnetPublishForReload,
409420
bool useDotnetBuildForReload,
410421
[CanBeNull] string targetFrameworkOverride,
411-
[CanBeNull] string selectedCustomJit,
422+
CompilerType selectedCompiler,
412423
int disassemblyTimeoutSeconds
413424
)
414425
{
@@ -421,12 +432,12 @@ int disassemblyTimeoutSeconds
421432
UseDotnetPublishForReload = useDotnetPublishForReload;
422433
UseDotnetBuildForReload = useDotnetBuildForReload;
423434
TargetFrameworkOverride = targetFrameworkOverride;
424-
SelectedCustomJit = selectedCustomJit;
435+
SelectedCompiler = selectedCompiler;
425436
DisassemblyTimeoutSeconds = disassemblyTimeoutSeconds;
426437
}
427438
//secondary constructor
428439
//deconstruct trait
429-
public void Deconstruct(out bool showAsmComments, out bool diffable, out bool useTieredJit, out bool usePGO, out bool runAppMode, out bool useNoRestoreFlag, out bool useDotnetPublishForReload, out bool useDotnetBuildForReload, [CanBeNull] out string targetFrameworkOverride, [CanBeNull] out string selectedCustomJit, out int disassemblyTimeoutSeconds)
440+
public void Deconstruct(out bool showAsmComments, out bool diffable, out bool useTieredJit, out bool usePGO, out bool runAppMode, out bool useNoRestoreFlag, out bool useDotnetPublishForReload, out bool useDotnetBuildForReload, [CanBeNull] out string targetFrameworkOverride, out CompilerType selectedCompiler, out int disassemblyTimeoutSeconds)
430441
{
431442
showAsmComments = ShowAsmComments;
432443
diffable = Diffable;
@@ -437,7 +448,7 @@ public void Deconstruct(out bool showAsmComments, out bool diffable, out bool us
437448
useDotnetPublishForReload = UseDotnetPublishForReload;
438449
useDotnetBuildForReload = UseDotnetBuildForReload;
439450
targetFrameworkOverride = TargetFrameworkOverride;
440-
selectedCustomJit = SelectedCustomJit;
451+
selectedCompiler = SelectedCompiler;
441452
disassemblyTimeoutSeconds = DisassemblyTimeoutSeconds;
442453
}
443454
//statics
@@ -453,9 +464,9 @@ public void Deconstruct(out bool showAsmComments, out bool diffable, out bool us
453464
var useDotnetPublishForReload = reader.ReadBool();
454465
var useDotnetBuildForReload = reader.ReadBool();
455466
var targetFrameworkOverride = ReadStringNullable(ctx, reader);
456-
var selectedCustomJit = ReadStringNullable(ctx, reader);
467+
var selectedCompiler = (CompilerType)reader.ReadInt();
457468
var disassemblyTimeoutSeconds = reader.ReadInt();
458-
var _result = new JitConfiguration(showAsmComments, diffable, useTieredJit, usePGO, runAppMode, useNoRestoreFlag, useDotnetPublishForReload, useDotnetBuildForReload, targetFrameworkOverride, selectedCustomJit, disassemblyTimeoutSeconds);
469+
var _result = new JitConfiguration(showAsmComments, diffable, useTieredJit, usePGO, runAppMode, useNoRestoreFlag, useDotnetPublishForReload, useDotnetBuildForReload, targetFrameworkOverride, selectedCompiler, disassemblyTimeoutSeconds);
459470
return _result;
460471
};
461472
public static CtxReadDelegate<string> ReadStringNullable = JetBrains.Rd.Impl.Serializers.ReadString.NullableClass();
@@ -471,7 +482,7 @@ public void Deconstruct(out bool showAsmComments, out bool diffable, out bool us
471482
writer.Write(value.UseDotnetPublishForReload);
472483
writer.Write(value.UseDotnetBuildForReload);
473484
WriteStringNullable(ctx, writer, value.TargetFrameworkOverride);
474-
WriteStringNullable(ctx, writer, value.SelectedCustomJit);
485+
writer.Write((int)value.SelectedCompiler);
475486
writer.Write(value.DisassemblyTimeoutSeconds);
476487
};
477488
public static CtxWriteDelegate<string> WriteStringNullable = JetBrains.Rd.Impl.Serializers.WriteString.NullableClass();
@@ -492,7 +503,7 @@ public bool Equals(JitConfiguration other)
492503
{
493504
if (ReferenceEquals(null, other)) return false;
494505
if (ReferenceEquals(this, other)) return true;
495-
return ShowAsmComments == other.ShowAsmComments && Diffable == other.Diffable && UseTieredJit == other.UseTieredJit && UsePGO == other.UsePGO && RunAppMode == other.RunAppMode && UseNoRestoreFlag == other.UseNoRestoreFlag && UseDotnetPublishForReload == other.UseDotnetPublishForReload && UseDotnetBuildForReload == other.UseDotnetBuildForReload && Equals(TargetFrameworkOverride, other.TargetFrameworkOverride) && Equals(SelectedCustomJit, other.SelectedCustomJit) && DisassemblyTimeoutSeconds == other.DisassemblyTimeoutSeconds;
506+
return ShowAsmComments == other.ShowAsmComments && Diffable == other.Diffable && UseTieredJit == other.UseTieredJit && UsePGO == other.UsePGO && RunAppMode == other.RunAppMode && UseNoRestoreFlag == other.UseNoRestoreFlag && UseDotnetPublishForReload == other.UseDotnetPublishForReload && UseDotnetBuildForReload == other.UseDotnetBuildForReload && Equals(TargetFrameworkOverride, other.TargetFrameworkOverride) && SelectedCompiler == other.SelectedCompiler && DisassemblyTimeoutSeconds == other.DisassemblyTimeoutSeconds;
496507
}
497508
//hash code trait
498509
public override int GetHashCode()
@@ -508,7 +519,7 @@ public override int GetHashCode()
508519
hash = hash * 31 + UseDotnetPublishForReload.GetHashCode();
509520
hash = hash * 31 + UseDotnetBuildForReload.GetHashCode();
510521
hash = hash * 31 + (TargetFrameworkOverride != null ? TargetFrameworkOverride.GetHashCode() : 0);
511-
hash = hash * 31 + (SelectedCustomJit != null ? SelectedCustomJit.GetHashCode() : 0);
522+
hash = hash * 31 + (int) SelectedCompiler;
512523
hash = hash * 31 + DisassemblyTimeoutSeconds.GetHashCode();
513524
return hash;
514525
}
@@ -527,7 +538,7 @@ public void Print(PrettyPrinter printer)
527538
printer.Print("useDotnetPublishForReload = "); UseDotnetPublishForReload.PrintEx(printer); printer.Println();
528539
printer.Print("useDotnetBuildForReload = "); UseDotnetBuildForReload.PrintEx(printer); printer.Println();
529540
printer.Print("targetFrameworkOverride = "); TargetFrameworkOverride.PrintEx(printer); printer.Println();
530-
printer.Print("selectedCustomJit = "); SelectedCustomJit.PrintEx(printer); printer.Println();
541+
printer.Print("selectedCompiler = "); SelectedCompiler.PrintEx(printer); printer.Println();
531542
printer.Print("disassemblyTimeoutSeconds = "); DisassemblyTimeoutSeconds.PrintEx(printer); printer.Println();
532543
}
533544
printer.Print(")");

0 commit comments

Comments
 (0)