Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions ReSharper.FSharp/ReSharper.FSharp.sln
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharp.Tests.Common", "test
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FSharp.RiderPlugin", "src\FSharp.RiderPlugin\proj\src\FSharp.RiderPlugin.csproj", "{F7735C4C-A866-4C77-9F6C-804791246580}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FSharp.Debugger", "src\FSharp.Debugger\FSharp.Debugger.csproj", "{0259EFB8-49A6-4851-AAB5-3839680BC70D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -121,6 +123,10 @@ Global
{F7735C4C-A866-4C77-9F6C-804791246580}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F7735C4C-A866-4C77-9F6C-804791246580}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F7735C4C-A866-4C77-9F6C-804791246580}.Release|Any CPU.Build.0 = Release|Any CPU
{0259EFB8-49A6-4851-AAB5-3839680BC70D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0259EFB8-49A6-4851-AAB5-3839680BC70D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0259EFB8-49A6-4851-AAB5-3839680BC70D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0259EFB8-49A6-4851-AAB5-3839680BC70D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -144,5 +150,6 @@ Global
{B5A9B84B-B69B-4DCD-863A-989023EAEF8C} = {7B41BA27-ACA5-43D2-9EBA-2E604D63A4E2}
{72982346-1D28-41CB-9CBF-056EC8482305} = {2E19336B-3133-4C1A-8E65-E2F7C465663B}
{F7735C4C-A866-4C77-9F6C-804791246580} = {2E19336B-3133-4C1A-8E65-E2F7C465663B}
{0259EFB8-49A6-4851-AAB5-3839680BC70D} = {2E19336B-3133-4C1A-8E65-E2F7C465663B}
EndGlobalSection
EndGlobal
6 changes: 6 additions & 0 deletions ReSharper.FSharp/src/FSharp.Debugger/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<Project>
<PropertyGroup>
<PackageLockFilePath>$(MSBuildThisFileDirectory)\PackagesLock.targets</PackageLockFilePath>
</PropertyGroup>
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
</Project>
30 changes: 30 additions & 0 deletions ReSharper.FSharp/src/FSharp.Debugger/FSharp.Debugger.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="../../Directory.Build.props" />
<PropertyGroup>
<InternalBuild Condition="'$(SolutionFileName)' != 'ReSharper.FSharp.sln'">True</InternalBuild>
<InternalBuild Condition="$(InternalBuild) == ''">False</InternalBuild>
</PropertyGroup>
<Import Project="Sdk.props" Sdk="JetBrains.Toolset.MainSolution.Sdk" Version="20200625.1.1.2" Condition="$(InternalBuild)" />
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" Condition="!$(InternalBuild)" />
<PropertyGroup>
<AssemblyName>JetBrains.ReSharper.Plugins.FSharp.Debugger</AssemblyName>
<LangVersion>$(CSharpLanguageVersion)</LangVersion>
<RootNamespace>JetBrains.ReSharper.Plugins.FSharp.Debugger</RootNamespace>
<TargetFramework>net472</TargetFramework>
</PropertyGroup>
<ItemGroup Label="PackageReference">
<PackageReference Include="Autofac" />
<PackageReference Include="JetBrains.ICorDebug"/>
<PackageReference Include="JetBrains.SharpGen.Runtime"/>
<PackageReference Include="JetBrains.SharpGen.Runtime.COM"/>
</ItemGroup>
<Target Name="DropObjFromIndependentBuild" Condition="$(InternalBuild)" BeforeTargets="Build">
<RemoveDir Directories="obj" />
</Target>
<Import Project="$(RiderBackendSubplatform)" Condition="Exists('$(RiderBackendSubplatform)') and !$(InternalBuild)" />
<Import Project="$(SolutionDir)/GitHubActions.targets" Condition="$(RunningOnGitHubActions) == 'true'" />
<Import Project="ManagedProject.Generated.Targets" Condition="$(InternalBuild)" />
<Import Project="Sdk.targets" Sdk="JetBrains.Toolset.MainSolution.Sdk" Version="20200625.1.1.2" Condition="$(InternalBuild)" />
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" Condition="!$(InternalBuild)" />
</Project>
15 changes: 15 additions & 0 deletions ReSharper.FSharp/src/FSharp.Debugger/PackagesLock.targets
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PackageLock Include="Autofac" Version="4.8.1" />
<PackageLock Include="JetBrains.Annotations" Version="2025.2.0" />
<PackageLock Include="JetBrains.HabitatDetector" Version="1.4.6" />
<PackageLock Include="JetBrains.ICorDebug" Version="2.0.20251125.126" />
<PackageLock Include="JetBrains.Lifetimes" Version="2026.1.1" />
<PackageLock Include="JetBrains.RdFramework" Version="2026.1.1" />
<PackageLock Include="JetBrains.SharpGen.Runtime" Version="2.0.20251125.126" />
<PackageLock Include="JetBrains.SharpGen.Runtime.COM" Version="2.0.20251125.126" />
<PackageLock Include="Newtonsoft.Json" Version="13.0.3" />
<PackageLock Include="System.Collections.Immutable" Version="8.0.0" />
</ItemGroup>
</Project>
8 changes: 8 additions & 0 deletions ReSharper.FSharp/src/FSharp.Debugger/Subplatform.Root
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<SubplatformInfo xmlns="urn:schemas-jetbrains-com:build-subplatform-info" CompanyNameHuman="JetBrains" IsGitSubplatform="true">
<SubplatformInfo.SubplatformReferences>
<SubplatformReference Name="Rider\Rider.Backend" />
<SubplatformReference Name="Debugger\Libs" />
<SubplatformReference Name="Plugins\_ReSharperFSharp.Pregenerated\BackendModel" />
</SubplatformInfo.SubplatformReferences>
</SubplatformInfo>
Binary file not shown.
3 changes: 3 additions & 0 deletions ReSharper.FSharp/src/FSharp.Debugger/src/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
using Mono.Debugging.Autofac;

[assembly:DebuggerAssembly]
18 changes: 18 additions & 0 deletions ReSharper.FSharp/src/FSharp.Debugger/src/FSharpMetadataUtil.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using JetBrains.Annotations;
using JetBrains.Metadata.Reader.API;

namespace JetBrains.ReSharper.Plugins.FSharp.Debugger;

public static class FSharpMetadataUtil
{
public static bool IsFSharpTypeFuncSpecialize([CanBeNull] this IMetadataMethod method)
{
if (method?.Name is not "Specialize")
return false;

var declaringType = method.DeclaringType;
if (declaringType.Assembly is not { AssemblyName.Name: "FSharp.Core" }) return false;

return declaringType.TypeName == "FSharpTypeFunc";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,284 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Debugger.Common.ManagedSymbols;
using JetBrains.Debugger.CorApi.ComInterop;
using JetBrains.Metadata.Access;
using JetBrains.Metadata.Debug;
using JetBrains.Metadata.Debug.Pdb;
using JetBrains.Metadata.Debug.Pdb.DebugSubsection;
using JetBrains.Metadata.IL;
using JetBrains.Metadata.Reader.API;
using JetBrains.Metadata.Utils;
using JetBrains.Metadata.Utils.PE.Directories;
using JetBrains.Util;
using Mono.Debugging.Autofac;
using Mono.Debugging.Client;
using Mono.Debugging.Win32;
using Mono.Debugging.Win32.Utils;

namespace JetBrains.ReSharper.Plugins.FSharp.Debugger;

[DebuggerSessionComponent(typeof(CorDebuggerType))]
public class FSharpSequencePointInvokedMethodsProvider(ILogger logger, CorDebuggerSession session)
: CorSequencePointInvokedMethodsProviderBase
{
public override int Priority => 100;

public override bool IsApplicable(IManagedSymbolDocument symbolDocument) =>
symbolDocument.LanguageId == Languages.FSharp;

public override bool IgnoreHiddenSequencePoints => true;

public override List<SmartStepIntoElement> GetSmartStepIntoElements(ICorDebugFunction function, uint startIp,
uint endIp, IMetadataMethod metadataMethod, IMetadataAssemblyInternals metadata,
TypeDecodeContext decodeContext)
{
var module = function.GetModule();
var moduleInfo = session.AppDomainsManager.GetOrCreateModuleInfo(module);

var debugData = PdbReader.ReadPdb(moduleInfo.AssemblyPath.ChangeExtension(ExtensionConstants.Pdb),
DebugInfoType.Portable);
var nameProvider = debugData != null ? new LocalVariablesNameProvider(debugData) : null;

var evalStack = new Stack<string>();
var elements = new List<SmartStepIntoElement>();

foreach (var instruction in function.EnumerateILInstructions(startIp, endIp))
{
switch (instruction.Code.Value)
{
case OpcodeValue.Call:
case OpcodeValue.Callvirt:
case OpcodeValue.Newobj:
try
{
var methodSpec = metadata.GetMethodFromToken((MetadataToken)instruction.Operand, decodeContext);
if (!CorSmartStepIntoProvider.TryCreateElement(methodSpec, instruction, logger, DebuggerHiddenProviders,
out var element))
return null;

if (element == null)
continue;

elements.Add(element);

var method = methodSpec.Method;
if (method.IsFSharpTypeFuncSpecialize())
{
element.IsHidden = true;
continue;
}

if (IsFSharpFuncInvoke(method))
{
var parametersCount = method.Parameters.Length;
var parametersToPopCount = method.IsStatic ? parametersCount - 1 : parametersCount;
for (var i = 0; i < parametersToPopCount; i++)
evalStack.Pop();

if (evalStack.Pop() is { } name)
element.SourceName = name;
}
else
{
for (var i = 0; i < method.Parameters.Length; i++)
evalStack.Pop();
}

if (!method.ReturnValue.Type.IsVoid() || method.IsConstructor())
evalStack.Push(null);
}
catch (Exception e)
{
logger.Error(e);
}

break;
case OpcodeValue.Calli:
break;

case OpcodeValue.Ldarg:
case OpcodeValue.Ldarg_0:
case OpcodeValue.Ldarg_1:
case OpcodeValue.Ldarg_2:
case OpcodeValue.Ldarg_3:
case OpcodeValue.Ldarg_s:
var variableIndex = instruction.GetVariableIndexOperand();
var index = metadataMethod.IsStatic ? variableIndex : variableIndex - 1;
var paramName = metadataMethod.Parameters.ElementAtOrDefault(index)?.Name;
evalStack.Push(paramName);
break;

case OpcodeValue.Ldloc:
case OpcodeValue.Ldloc_0:
case OpcodeValue.Ldloc_1:
case OpcodeValue.Ldloc_2:
case OpcodeValue.Ldloc_3:
case OpcodeValue.Ldloc_s:
var varIndex = instruction.GetVariableIndexOperand();
var variableName = nameProvider?.GetVariableName(metadataMethod, varIndex);
evalStack.Push(variableName);
break;

case OpcodeValue.Ldfld:
case OpcodeValue.Ldsfld:
var fieldSpec = metadata.GetFieldFromToken(instruction.OperandToken, decodeContext);
evalStack.Push(fieldSpec.Field.Name);
break;

case OpcodeValue.Add:
case OpcodeValue.Add_ovf:
case OpcodeValue.Add_ovf_un:
case OpcodeValue.And:
case OpcodeValue.Brfalse:
case OpcodeValue.Brfalse_s:
case OpcodeValue.Brtrue:
case OpcodeValue.Brtrue_s:
case OpcodeValue.Ceq:
case OpcodeValue.Cgt:
case OpcodeValue.Cgt_un:
case OpcodeValue.Clt:
case OpcodeValue.Clt_un:
case OpcodeValue.Div:
case OpcodeValue.Div_un:
case OpcodeValue.Endfilter:
case OpcodeValue.Initobj:
case OpcodeValue.Ldelem:
case OpcodeValue.Ldelem_i:
case OpcodeValue.Ldelem_i1:
case OpcodeValue.Ldelem_i2:
case OpcodeValue.Ldelem_i4:
case OpcodeValue.Ldelem_i8:
case OpcodeValue.Ldelem_u1:
case OpcodeValue.Ldelem_u2:
case OpcodeValue.Ldelem_u4:
case OpcodeValue.Ldelem_r4:
case OpcodeValue.Ldelem_r8:
case OpcodeValue.Ldelem_ref:
case OpcodeValue.Ldelema:
case OpcodeValue.Mul:
case OpcodeValue.Mul_ovf:
case OpcodeValue.Mul_ovf_un:
case OpcodeValue.Or:
case OpcodeValue.Pop:
case OpcodeValue.Rem:
case OpcodeValue.Rem_un:
case OpcodeValue.Shl:
case OpcodeValue.Shr:
case OpcodeValue.Shr_un:
case OpcodeValue.Starg:
case OpcodeValue.Starg_s:
case OpcodeValue.Stloc:
case OpcodeValue.Stloc_0:
case OpcodeValue.Stloc_1:
case OpcodeValue.Stloc_2:
case OpcodeValue.Stloc_3:
case OpcodeValue.Stloc_s:
case OpcodeValue.Sub:
case OpcodeValue.Sub_ovf:
case OpcodeValue.Sub_ovf_un:
case OpcodeValue.Switch:
case OpcodeValue.Throw:
case OpcodeValue.Xor:
evalStack.Pop();
break;

case OpcodeValue.Beq:
case OpcodeValue.Beq_s:
case OpcodeValue.Bge:
case OpcodeValue.Bge_s:
case OpcodeValue.Bge_un_s:
case OpcodeValue.Bge_un:
case OpcodeValue.Bgt:
case OpcodeValue.Bgt_s:
case OpcodeValue.Bgt_un:
case OpcodeValue.Bgt_un_s:
case OpcodeValue.Ble:
case OpcodeValue.Ble_s:
case OpcodeValue.Ble_un:
case OpcodeValue.Ble_un_s:
case OpcodeValue.Blt:
case OpcodeValue.Blt_s:
case OpcodeValue.Blt_un:
case OpcodeValue.Blt_un_s:
case OpcodeValue.Bne_un:
case OpcodeValue.Bne_un_s:
case OpcodeValue.Cpobj:
case OpcodeValue.Stind_i1:
case OpcodeValue.Stind_i2:
case OpcodeValue.Stind_i4:
case OpcodeValue.Stind_i8:
case OpcodeValue.Stind_r4:
case OpcodeValue.Stind_r8:
case OpcodeValue.Stind_i:
case OpcodeValue.Stind_ref:
case OpcodeValue.Stfld:
case OpcodeValue.Stobj:
evalStack.Pop();
evalStack.Pop();
break;

case OpcodeValue.Stelem:
case OpcodeValue.Stelem_i:
case OpcodeValue.Stelem_i1:
case OpcodeValue.Stelem_i2:
case OpcodeValue.Stelem_i4:
case OpcodeValue.Stelem_i8:
case OpcodeValue.Stelem_r4:
case OpcodeValue.Stelem_r8:
case OpcodeValue.Stelem_ref:
case OpcodeValue.Cpblk:
case OpcodeValue.Initblk:
evalStack.Pop();
evalStack.Pop();
evalStack.Pop();
break;

case OpcodeValue.Arglist:
case OpcodeValue.Dup:
case OpcodeValue.Ldarga:
case OpcodeValue.Ldarga_s:
case OpcodeValue.Ldc_i4:
case OpcodeValue.Ldc_i4_0:
case OpcodeValue.Ldc_i4_1:
case OpcodeValue.Ldc_i4_2:
case OpcodeValue.Ldc_i4_3:
case OpcodeValue.Ldc_i4_4:
case OpcodeValue.Ldc_i4_5:
case OpcodeValue.Ldc_i4_6:
case OpcodeValue.Ldc_i4_7:
case OpcodeValue.Ldc_i4_8:
case OpcodeValue.Ldc_i4_m1:
case OpcodeValue.Ldc_i4_s:
case OpcodeValue.Ldc_i8:
case OpcodeValue.Ldc_r4:
case OpcodeValue.Ldc_r8:
case OpcodeValue.Ldftn:
case OpcodeValue.Ldloca:
case OpcodeValue.Ldloca_s:
case OpcodeValue.Ldnull:
case OpcodeValue.Ldflda:
case OpcodeValue.Ldsflda:
case OpcodeValue.Ldstr:
case OpcodeValue.Ldtoken:
case OpcodeValue.Sizeof:
evalStack.Push(null);
break;
}
}

return elements;
}

private static bool IsFSharpFuncInvoke(IMetadataMethod method)
{
if (method?.Name is not ("Invoke" or "InvokeFast"))
return false;

var declaringType = method.DeclaringType;
if (declaringType.Assembly is not { AssemblyName.Name: "FSharp.Core" }) return false;

return declaringType.TypeParametersCountStrippedShortName() == "FSharpFunc";
}
}
Loading
Loading