mirror of
https://github.com/Smaug123/WoofWare.PawPrint
synced 2025-10-06 14:38:40 +00:00
Compare commits
1 Commits
8112b122fb
...
expand-exc
Author | SHA1 | Date | |
---|---|---|---|
|
56fbf7e398 |
@@ -193,6 +193,24 @@ module TestPureCases =
|
||||
NativeImpls = MockEnv.make ()
|
||||
LocalVariablesOfMain = [ CliType.Numeric (CliNumericType.Int32 10) ] |> Some
|
||||
}
|
||||
{
|
||||
FileName = "ExceptionWithNestedHandlers.cs"
|
||||
ExpectedReturnCode = 10112
|
||||
NativeImpls = NativeImpls.PassThru ()
|
||||
LocalVariablesOfMain =
|
||||
[ 10112 ]
|
||||
|> List.map (fun i -> CliType.Numeric (CliNumericType.Int32 i))
|
||||
|> Some
|
||||
}
|
||||
{
|
||||
FileName = "ExceptionWithTypeMatching.cs"
|
||||
ExpectedReturnCode = 10112
|
||||
NativeImpls = NativeImpls.PassThru ()
|
||||
LocalVariablesOfMain =
|
||||
[ 10112 ]
|
||||
|> List.map (fun i -> CliType.Numeric (CliNumericType.Int32 i))
|
||||
|> Some
|
||||
}
|
||||
{
|
||||
FileName = "Floats.cs"
|
||||
ExpectedReturnCode = 0
|
||||
|
@@ -26,6 +26,8 @@
|
||||
<EmbeddedResource Include="sourcesPure\Ldelema.cs" />
|
||||
<EmbeddedResource Include="sourcesPure\ExceptionWithNoOpCatch.cs" />
|
||||
<EmbeddedResource Include="sourcesPure\ExceptionWithNoOpFinally.cs" />
|
||||
<EmbeddedResource Include="sourcesPure\ExceptionWithNestedHandlers.cs" />
|
||||
<EmbeddedResource Include="sourcesPure\ExceptionWithTypeMatching.cs" />
|
||||
<EmbeddedResource Include="sourcesPure\TryCatchWithThrowInBody.cs" />
|
||||
<EmbeddedResource Include="sourcesPure\ComplexTryCatch.cs" />
|
||||
<EmbeddedResource Include="sourcesPure\TriangleNumber.cs" />
|
||||
|
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
|
||||
namespace HelloWorld
|
||||
{
|
||||
class Program {
|
||||
static int Test_NestedHandlers()
|
||||
{
|
||||
int x = 0;
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
x = 1;
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
x = 2;
|
||||
throw new ArgumentException(); // Re-throw different exception
|
||||
}
|
||||
finally
|
||||
{
|
||||
x += 10; // Inner finally
|
||||
}
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
x += 100; // Outer catch
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
x += 1000; // This should NOT execute
|
||||
}
|
||||
finally
|
||||
{
|
||||
x += 10000; // Outer finally
|
||||
}
|
||||
|
||||
return x; // Expected: 10112 (1 -> 2 -> 12 -> 112 -> 10112)
|
||||
}
|
||||
|
||||
static int Main(string[] args)
|
||||
{
|
||||
return Test_NestedHandlers();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
|
||||
namespace HelloWorld
|
||||
{
|
||||
class Program {
|
||||
static int Test_TypeMatching()
|
||||
{
|
||||
int x = 0;
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
x = 1;
|
||||
throw new ArgumentNullException(); // Derives from ArgumentException
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
x = 999; // Should NOT execute
|
||||
}
|
||||
catch (ArgumentException) // This should catch ArgumentNullException
|
||||
{
|
||||
x = 2;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
x = 888; // Should NOT execute (more general handler)
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
x = 777; // Should NOT execute (outer handler)
|
||||
}
|
||||
return x; // Expected: 2
|
||||
}
|
||||
|
||||
static int Main(string[] args)
|
||||
{
|
||||
return Test_TypeMatching();
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,32 +1,29 @@
|
||||
namespace WoofWare.PawPrint
|
||||
|
||||
open System
|
||||
open System.Collections.Immutable
|
||||
|
||||
/// Represents a location in the code where an exception occurred
|
||||
type ExceptionStackFrame<'typeGen, 'methodGen, 'methodVar
|
||||
when 'typeGen : comparison and 'typeGen :> IComparable<'typeGen>> =
|
||||
type ExceptionStackFrame =
|
||||
{
|
||||
Method : WoofWare.PawPrint.MethodInfo<'typeGen, 'methodGen, 'methodVar>
|
||||
Method : WoofWare.PawPrint.MethodInfo<TypeDefn, TypeDefn>
|
||||
/// The number of bytes into the IL of the method we were in
|
||||
IlOffset : int
|
||||
}
|
||||
|
||||
/// Represents a CLI exception being propagated
|
||||
type CliException<'typeGen, 'methodGen, 'methodVar when 'typeGen : comparison and 'typeGen :> IComparable<'typeGen>> =
|
||||
type CliException =
|
||||
{
|
||||
/// The exception object allocated on the heap
|
||||
ExceptionObject : ManagedHeapAddress
|
||||
/// Stack trace built during unwinding
|
||||
StackTrace : ExceptionStackFrame<'typeGen, 'methodGen, 'methodVar> list
|
||||
StackTrace : ExceptionStackFrame list
|
||||
}
|
||||
|
||||
/// Represents what to do after executing a finally/filter block
|
||||
type ExceptionContinuation<'typeGen, 'methodGen, 'methodVar
|
||||
when 'typeGen : comparison and 'typeGen :> IComparable<'typeGen>> =
|
||||
type ExceptionContinuation =
|
||||
| ResumeAfterFinally of targetPC : int
|
||||
| PropagatingException of exn : CliException<'typeGen, 'methodGen, 'methodVar>
|
||||
| ResumeAfterFilter of handlerPC : int * exn : CliException<'typeGen, 'methodGen, 'methodVar>
|
||||
| PropagatingException of exn : CliException
|
||||
| ResumeAfterFilter of handlerPC : int * exn : CliException
|
||||
|
||||
/// Helper functions for exception handling
|
||||
[<RequireQualifiedAccess>]
|
||||
@@ -47,7 +44,7 @@ module ExceptionHandling =
|
||||
let findExceptionHandler
|
||||
(currentPC : int)
|
||||
(exceptionTypeCrate : TypeInfoCrate)
|
||||
(method : WoofWare.PawPrint.MethodInfo<'typeGen, 'methodGeneric, 'methodVar>)
|
||||
(method : WoofWare.PawPrint.MethodInfo<TypeDefn, 'methodGeneric>)
|
||||
(assemblies : ImmutableDictionary<string, DumpedAssembly>)
|
||||
: (WoofWare.PawPrint.ExceptionRegion * bool) option // handler, isFinally
|
||||
=
|
||||
@@ -56,6 +53,7 @@ module ExceptionHandling =
|
||||
| Some instructions ->
|
||||
|
||||
// Find all handlers that cover the current PC
|
||||
let handlers =
|
||||
instructions.ExceptionRegions
|
||||
|> Seq.choose (fun region ->
|
||||
match region with
|
||||
@@ -63,7 +61,7 @@ module ExceptionHandling =
|
||||
if currentPC >= offset.TryOffset && currentPC < offset.TryOffset + offset.TryLength then
|
||||
// Check if exception type matches
|
||||
if isExceptionAssignableTo exceptionTypeCrate typeToken assemblies then
|
||||
Some (region, false)
|
||||
Some (region, false, offset.TryOffset, offset.TryLength)
|
||||
else
|
||||
None
|
||||
else
|
||||
@@ -73,29 +71,50 @@ module ExceptionHandling =
|
||||
failwith "TODO: filter needs to be evaluated"
|
||||
else
|
||||
None
|
||||
| ExceptionRegion.Finally offset ->
|
||||
// Don't return finally blocks here - they're handled separately
|
||||
None
|
||||
| ExceptionRegion.Fault offset ->
|
||||
// Fault blocks are only executed when propagating exceptions
|
||||
None
|
||||
)
|
||||
|> Seq.toList
|
||||
|
||||
// If multiple catch handlers, return the innermost one (highest TryOffset)
|
||||
match handlers with
|
||||
| [] ->
|
||||
// No catch/filter handler found, check for finally/fault blocks
|
||||
// that need to run while propagating
|
||||
instructions.ExceptionRegions
|
||||
|> Seq.choose (fun region ->
|
||||
match region with
|
||||
| ExceptionRegion.Finally offset ->
|
||||
if currentPC >= offset.TryOffset && currentPC < offset.TryOffset + offset.TryLength then
|
||||
Some (region, true)
|
||||
Some (region, true, offset.TryOffset, offset.TryLength)
|
||||
else
|
||||
None
|
||||
| ExceptionRegion.Fault offset ->
|
||||
if currentPC >= offset.TryOffset && currentPC < offset.TryOffset + offset.TryLength then
|
||||
Some (region, true)
|
||||
Some (region, true, offset.TryOffset, offset.TryLength)
|
||||
else
|
||||
None
|
||||
| _ -> None
|
||||
)
|
||||
|> Seq.toList
|
||||
|> fun x ->
|
||||
match x with
|
||||
| [] -> None
|
||||
| [ x ] -> Some x
|
||||
| _ -> failwith "multiple exception regions"
|
||||
|> Seq.sortByDescending (fun (_, _, tryOffset, _) -> tryOffset)
|
||||
|> Seq.tryHead
|
||||
|> Option.map (fun (region, isFinally, _, _) -> (region, isFinally))
|
||||
| handlers ->
|
||||
// Return the innermost handler (highest TryOffset, or smallest TryLength for same offset)
|
||||
handlers
|
||||
|> List.sortBy (fun (_, _, tryOffset, tryLength) -> (-tryOffset, tryLength))
|
||||
|> List.head
|
||||
|> fun (region, isFinally, _, _) -> Some (region, isFinally)
|
||||
|
||||
/// Find finally blocks that need to run when leaving a try region
|
||||
let findFinallyBlocksToRun
|
||||
(currentPC : int)
|
||||
(targetPC : int)
|
||||
(method : WoofWare.PawPrint.MethodInfo<'typeGeneric, 'methodGeneric, 'methodVar>)
|
||||
(method : WoofWare.PawPrint.MethodInfo<TypeDefn, 'methodGeneric>)
|
||||
: ExceptionOffset list
|
||||
=
|
||||
match method.Instructions with
|
||||
@@ -125,7 +144,7 @@ module ExceptionHandling =
|
||||
/// Get the active exception regions at a given offset
|
||||
let getActiveRegionsAtOffset
|
||||
(offset : int)
|
||||
(method : WoofWare.PawPrint.MethodInfo<'a, 'b, 'c>)
|
||||
(method : WoofWare.PawPrint.MethodInfo<TypeDefn, 'methodGeneric>)
|
||||
: WoofWare.PawPrint.ExceptionRegion list
|
||||
=
|
||||
match method.Instructions with
|
||||
|
Reference in New Issue
Block a user