mirror of
https://github.com/Smaug123/WoofWare.PawPrint
synced 2025-12-15 14:05:41 +00:00
Compare commits
1 Commits
castclass
...
expand-exc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
56fbf7e398 |
@@ -92,132 +92,6 @@ module TestPureCases =
|
||||
NativeImpls = MockEnv.make ()
|
||||
LocalVariablesOfMain = [ CliType.Numeric (CliNumericType.Int32 1) ] |> Some
|
||||
}
|
||||
{
|
||||
FileName = "CastClassSimpleInheritance.cs"
|
||||
ExpectedReturnCode = 5
|
||||
NativeImpls = MockEnv.make ()
|
||||
LocalVariablesOfMain = None
|
||||
}
|
||||
{
|
||||
FileName = "IsInstSimpleInheritance.cs"
|
||||
ExpectedReturnCode = 42
|
||||
NativeImpls = MockEnv.make ()
|
||||
LocalVariablesOfMain = None
|
||||
}
|
||||
{
|
||||
FileName = "CastClassNull.cs"
|
||||
ExpectedReturnCode = 42
|
||||
NativeImpls = MockEnv.make ()
|
||||
LocalVariablesOfMain = None
|
||||
}
|
||||
{
|
||||
FileName = "CastClassArrayCovariance.cs"
|
||||
ExpectedReturnCode = 1
|
||||
NativeImpls = MockEnv.make ()
|
||||
LocalVariablesOfMain = None
|
||||
}
|
||||
{
|
||||
FileName = "CastClassToObject.cs"
|
||||
ExpectedReturnCode = 1
|
||||
NativeImpls = MockEnv.make ()
|
||||
LocalVariablesOfMain = None
|
||||
}
|
||||
{
|
||||
FileName = "IsinstPatternMatching.cs"
|
||||
ExpectedReturnCode = 1
|
||||
NativeImpls = MockEnv.make ()
|
||||
LocalVariablesOfMain = None
|
||||
}
|
||||
{
|
||||
FileName = "CastClassMultipleInterfaces.cs"
|
||||
ExpectedReturnCode = 42
|
||||
NativeImpls = MockEnv.make ()
|
||||
LocalVariablesOfMain = None
|
||||
}
|
||||
{
|
||||
FileName = "CastClassCrossAssembly.cs"
|
||||
ExpectedReturnCode = 1
|
||||
NativeImpls = MockEnv.make ()
|
||||
LocalVariablesOfMain = None
|
||||
}
|
||||
{
|
||||
FileName = "CastClassNestedTypes.cs"
|
||||
ExpectedReturnCode = 1
|
||||
NativeImpls = MockEnv.make ()
|
||||
LocalVariablesOfMain = None
|
||||
}
|
||||
{
|
||||
FileName = "CastClassGenerics.cs"
|
||||
ExpectedReturnCode = 1
|
||||
NativeImpls = MockEnv.make ()
|
||||
LocalVariablesOfMain = None
|
||||
}
|
||||
{
|
||||
FileName = "CastClassEnum.cs"
|
||||
ExpectedReturnCode = 1
|
||||
NativeImpls = MockEnv.make ()
|
||||
LocalVariablesOfMain = None
|
||||
}
|
||||
{
|
||||
FileName = "CastClassBoxing.cs"
|
||||
ExpectedReturnCode = 1
|
||||
NativeImpls = MockEnv.make ()
|
||||
LocalVariablesOfMain = None
|
||||
}
|
||||
{
|
||||
FileName = "IsinstBoxing.cs"
|
||||
ExpectedReturnCode = 1
|
||||
NativeImpls = MockEnv.make ()
|
||||
LocalVariablesOfMain = None
|
||||
}
|
||||
{
|
||||
FileName = "CastClassArray.cs"
|
||||
ExpectedReturnCode = 1
|
||||
NativeImpls = MockEnv.make ()
|
||||
LocalVariablesOfMain = None
|
||||
}
|
||||
{
|
||||
FileName = "IsinstArray.cs"
|
||||
ExpectedReturnCode = 1
|
||||
NativeImpls = MockEnv.make ()
|
||||
LocalVariablesOfMain = None
|
||||
}
|
||||
{
|
||||
FileName = "IsinstNull.cs"
|
||||
ExpectedReturnCode = 42
|
||||
NativeImpls = MockEnv.make ()
|
||||
LocalVariablesOfMain = None
|
||||
}
|
||||
{
|
||||
FileName = "CastClassInvalid.cs"
|
||||
ExpectedReturnCode = 1
|
||||
NativeImpls = MockEnv.make ()
|
||||
LocalVariablesOfMain = None
|
||||
}
|
||||
{
|
||||
FileName = "IsinstFailed.cs"
|
||||
ExpectedReturnCode = 1
|
||||
NativeImpls = MockEnv.make ()
|
||||
LocalVariablesOfMain = None
|
||||
}
|
||||
{
|
||||
FileName = "IsinstFailedInterface.cs"
|
||||
ExpectedReturnCode = 1
|
||||
NativeImpls = MockEnv.make ()
|
||||
LocalVariablesOfMain = None
|
||||
}
|
||||
{
|
||||
FileName = "CastClassInterface.cs"
|
||||
ExpectedReturnCode = 1
|
||||
NativeImpls = MockEnv.make ()
|
||||
LocalVariablesOfMain = None
|
||||
}
|
||||
{
|
||||
FileName = "IsinstInterface.cs"
|
||||
ExpectedReturnCode = 42
|
||||
NativeImpls = MockEnv.make ()
|
||||
LocalVariablesOfMain = None
|
||||
}
|
||||
{
|
||||
FileName = "StaticVariables.cs"
|
||||
ExpectedReturnCode = 0
|
||||
@@ -319,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,33 +26,14 @@
|
||||
<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" />
|
||||
<EmbeddedResource Include="sourcesPure\Threads.cs" />
|
||||
<EmbeddedResource Include="sourcesPure\ResizeArray.cs" />
|
||||
<EmbeddedResource Include="sourcesPure\ArgumentOrdering.cs" />
|
||||
<EmbeddedResource Include="sourcesPure\CastClassSimpleInheritance.cs" />
|
||||
<EmbeddedResource Include="sourcesPure\IsInstSimpleInheritance.cs" />
|
||||
<EmbeddedResource Include="sourcesPure\CastClassNull.cs" />
|
||||
<EmbeddedResource Include="sourcesPure\CastClassArrayCovariance.cs" />
|
||||
<EmbeddedResource Include="sourcesPure\CastClassToObject.cs" />
|
||||
<EmbeddedResource Include="sourcesPure\IsinstPatternMatching.cs" />
|
||||
<EmbeddedResource Include="sourcesPure\CastClassMultipleInterfaces.cs" />
|
||||
<EmbeddedResource Include="sourcesPure\CastClassCrossAssembly.cs" />
|
||||
<EmbeddedResource Include="sourcesPure\CastClassNestedTypes.cs" />
|
||||
<EmbeddedResource Include="sourcesPure\CastClassGenerics.cs" />
|
||||
<EmbeddedResource Include="sourcesPure\CastClassEnum.cs" />
|
||||
<EmbeddedResource Include="sourcesPure\CastClassBoxing.cs" />
|
||||
<EmbeddedResource Include="sourcesPure\IsinstBoxing.cs" />
|
||||
<EmbeddedResource Include="sourcesPure\CastClassArray.cs" />
|
||||
<EmbeddedResource Include="sourcesPure\IsinstArray.cs" />
|
||||
<EmbeddedResource Include="sourcesPure\IsinstNull.cs" />
|
||||
<EmbeddedResource Include="sourcesPure\CastClassInvalid.cs" />
|
||||
<EmbeddedResource Include="sourcesPure\IsinstFailed.cs" />
|
||||
<EmbeddedResource Include="sourcesPure\IsinstFailedInterface.cs" />
|
||||
<EmbeddedResource Include="sourcesPure\CastClassInterface.cs" />
|
||||
<EmbeddedResource Include="sourcesPure\IsinstInterface.cs" />
|
||||
<EmbeddedResource Include="sourcesPure\TestShl.cs" />
|
||||
<EmbeddedResource Include="sourcesPure\TestShr.cs" />
|
||||
<EmbeddedResource Include="sourcesPure\TestOr.cs" />
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
public class Program
|
||||
{
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
int[] numbers = new int[] { 1, 2, 3, 4, 5 };
|
||||
|
||||
// Cast array to System.Array - should succeed
|
||||
System.Array array = (System.Array)numbers;
|
||||
|
||||
return array.Length;
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
public class Program
|
||||
{
|
||||
public struct Counter
|
||||
{
|
||||
public int Count;
|
||||
|
||||
public Counter(int count)
|
||||
{
|
||||
Count = count;
|
||||
}
|
||||
}
|
||||
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
Counter counter = new Counter(42);
|
||||
|
||||
// Box the value type
|
||||
object boxed = counter;
|
||||
|
||||
// Check if boxed value is System.ValueType
|
||||
if (boxed is System.ValueType)
|
||||
{
|
||||
return 42;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
public class Program
|
||||
{
|
||||
public struct Point
|
||||
{
|
||||
public int X;
|
||||
public int Y;
|
||||
|
||||
public Point(int x, int y)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
}
|
||||
}
|
||||
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
Point p = new Point(10, 32);
|
||||
|
||||
// Box the value type
|
||||
object boxed = p;
|
||||
|
||||
// Cast boxed value type to object (should succeed)
|
||||
object obj = (object)boxed;
|
||||
|
||||
// Unbox
|
||||
Point unboxed = (Point)obj;
|
||||
|
||||
return unboxed.X + unboxed.Y;
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class Program
|
||||
{
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
// Using types from System.Collections.Generic assembly
|
||||
List<int> list = new List<int> { 1, 2, 3, 4, 5 };
|
||||
|
||||
// Cast to interface from another assembly
|
||||
IEnumerable<int> enumerable = (IEnumerable<int>)list;
|
||||
|
||||
int count = 0;
|
||||
foreach (var item in enumerable)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
|
||||
return count == 5 ? 42 : 0;
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
public class Program
|
||||
{
|
||||
public enum Color
|
||||
{
|
||||
Red = 1,
|
||||
Green = 2,
|
||||
Blue = 42
|
||||
}
|
||||
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
Color myColor = Color.Blue;
|
||||
|
||||
// Box enum value
|
||||
object boxed = myColor;
|
||||
|
||||
// Cast to System.Enum
|
||||
System.Enum enumValue = (System.Enum)boxed;
|
||||
|
||||
// Cast back to specific enum
|
||||
Color unboxed = (Color)enumValue;
|
||||
|
||||
return (int)unboxed;
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
public class Program
|
||||
{
|
||||
public class Container<T>
|
||||
{
|
||||
public T Value { get; set; }
|
||||
}
|
||||
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
Container<int> intContainer = new Container<int> { Value = 42 };
|
||||
|
||||
// Cast generic type to object
|
||||
object obj = (object)intContainer;
|
||||
|
||||
// Check type and cast back
|
||||
if (obj is Container<int> container)
|
||||
{
|
||||
return container.Value;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
public class Program
|
||||
{
|
||||
public interface ICalculator
|
||||
{
|
||||
int Calculate(int x, int y);
|
||||
}
|
||||
|
||||
public class Adder : ICalculator
|
||||
{
|
||||
public int Calculate(int x, int y)
|
||||
{
|
||||
return x + y;
|
||||
}
|
||||
}
|
||||
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
Adder adder = new Adder();
|
||||
|
||||
// Cast to interface - should succeed
|
||||
ICalculator calc = (ICalculator)adder;
|
||||
|
||||
return calc.Calculate(10, 32);
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
public class Program
|
||||
{
|
||||
public class Cat
|
||||
{
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
public class Dog
|
||||
{
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
object cat = new Cat { Name = "Whiskers" };
|
||||
|
||||
// Invalid cast - should throw InvalidCastException
|
||||
Dog dog = (Dog)cat;
|
||||
|
||||
// Should not reach here
|
||||
return 0;
|
||||
}
|
||||
catch (System.InvalidCastException)
|
||||
{
|
||||
// Expected exception caught
|
||||
return 42;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
public class Program
|
||||
{
|
||||
public interface IReadable
|
||||
{
|
||||
string Read();
|
||||
}
|
||||
|
||||
public interface IWritable
|
||||
{
|
||||
void Write(string data);
|
||||
}
|
||||
|
||||
public class File : IReadable, IWritable
|
||||
{
|
||||
private string content = "Hello";
|
||||
|
||||
public string Read()
|
||||
{
|
||||
return content;
|
||||
}
|
||||
|
||||
public void Write(string data)
|
||||
{
|
||||
content = data;
|
||||
}
|
||||
}
|
||||
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
File file = new File();
|
||||
|
||||
// Cast to first interface
|
||||
IReadable readable = (IReadable)file;
|
||||
|
||||
// Cast to second interface
|
||||
IWritable writable = (IWritable)file;
|
||||
|
||||
return readable != null && writable != null ? 42 : 0;
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
public class Program
|
||||
{
|
||||
public class Outer
|
||||
{
|
||||
public class Inner
|
||||
{
|
||||
public int Value { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
Outer.Inner inner = new Outer.Inner { Value = 42 };
|
||||
|
||||
// Cast nested type to object
|
||||
object obj = (object)inner;
|
||||
|
||||
// Cast back
|
||||
Outer.Inner casted = (Outer.Inner)obj;
|
||||
|
||||
return casted.Value;
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
public class Program
|
||||
{
|
||||
public class MyClass
|
||||
{
|
||||
public int Value { get; set; }
|
||||
}
|
||||
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
MyClass obj = null;
|
||||
|
||||
// Cast null reference - should succeed and remain null
|
||||
object result = (object)obj;
|
||||
|
||||
return result == null ? 42 : 0;
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
public class Program
|
||||
{
|
||||
public class Animal
|
||||
{
|
||||
public int Age { get; set; }
|
||||
}
|
||||
|
||||
public class Dog : Animal
|
||||
{
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
Dog myDog = new Dog { Age = 5, Name = "Rex" };
|
||||
|
||||
// Cast to base class - should succeed
|
||||
Animal animal = (Animal)myDog;
|
||||
|
||||
return animal.Age;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
public class Program
|
||||
{
|
||||
public class CustomClass
|
||||
{
|
||||
public int Id { get; set; }
|
||||
}
|
||||
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
CustomClass custom = new CustomClass { Id = 42 };
|
||||
|
||||
// Everything can be cast to System.Object
|
||||
System.Object obj = (System.Object)custom;
|
||||
|
||||
// Verify it's the same object
|
||||
return obj != null && obj == custom ? 42 : 0;
|
||||
}
|
||||
}
|
||||
@@ -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,25 +0,0 @@
|
||||
public class Program
|
||||
{
|
||||
public class Vehicle
|
||||
{
|
||||
public int Wheels { get; set; }
|
||||
}
|
||||
|
||||
public class Car : Vehicle
|
||||
{
|
||||
public string Model { get; set; }
|
||||
}
|
||||
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
Car myCar = new Car { Wheels = 4, Model = "Tesla" };
|
||||
|
||||
// 'is' operator uses isinst instruction
|
||||
if (myCar is Vehicle)
|
||||
{
|
||||
return 42;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
public class Program
|
||||
{
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
string[] names = new string[] { "Alice", "Bob", "Charlie" };
|
||||
|
||||
// Check if array is System.Array
|
||||
if (names is System.Array)
|
||||
{
|
||||
return 42;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
public class Program
|
||||
{
|
||||
public struct Counter
|
||||
{
|
||||
public int Count;
|
||||
|
||||
public Counter(int count)
|
||||
{
|
||||
Count = count;
|
||||
}
|
||||
}
|
||||
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
Counter counter = new Counter(42);
|
||||
|
||||
// Box the value type
|
||||
object boxed = counter;
|
||||
|
||||
// Check if boxed value is System.ValueType
|
||||
if (boxed is System.ValueType)
|
||||
{
|
||||
return 42;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
public class Program
|
||||
{
|
||||
public class Bird
|
||||
{
|
||||
public bool CanFly { get; set; }
|
||||
}
|
||||
|
||||
public class Fish
|
||||
{
|
||||
public bool CanSwim { get; set; }
|
||||
}
|
||||
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
Bird sparrow = new Bird { CanFly = true };
|
||||
|
||||
// Cast to object first to bypass compile-time checking
|
||||
object obj = sparrow;
|
||||
|
||||
// This should fail at runtime and return null (not throw)
|
||||
Fish fish = obj as Fish;
|
||||
|
||||
return fish == null ? 42 : 0;
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
public class Program
|
||||
{
|
||||
public interface IAnimal
|
||||
{
|
||||
string Name { get; set; }
|
||||
}
|
||||
|
||||
public class Bird : IAnimal
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public bool CanFly { get; set; }
|
||||
}
|
||||
|
||||
public class Fish : IAnimal
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public bool CanSwim { get; set; }
|
||||
}
|
||||
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
IAnimal animal = new Bird { Name = "Sparrow", CanFly = true };
|
||||
|
||||
// This should fail at runtime and return null (not throw)
|
||||
// because the actual object is Bird, not Fish
|
||||
Fish fish = animal as Fish;
|
||||
|
||||
return fish == null ? 42 : 0;
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
public class Program
|
||||
{
|
||||
public interface IDisposable
|
||||
{
|
||||
void Dispose();
|
||||
}
|
||||
|
||||
public class Resource : IDisposable
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Cleanup
|
||||
}
|
||||
}
|
||||
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
Resource resource = new Resource { Id = 42 };
|
||||
|
||||
// 'as' operator uses isinst instruction
|
||||
IDisposable disposable = resource as IDisposable;
|
||||
|
||||
return disposable != null ? resource.Id : 0;
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
public class Program
|
||||
{
|
||||
public class MyType
|
||||
{
|
||||
public int Value { get; set; }
|
||||
}
|
||||
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
MyType obj = null;
|
||||
|
||||
// isinst on null should return null
|
||||
object result = obj as object;
|
||||
|
||||
return result == null ? 42 : 0;
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
public class Program
|
||||
{
|
||||
public abstract class Shape
|
||||
{
|
||||
public abstract double GetArea();
|
||||
}
|
||||
|
||||
public class Circle : Shape
|
||||
{
|
||||
public double Radius { get; set; }
|
||||
|
||||
public override double GetArea()
|
||||
{
|
||||
return 3.14 * Radius * Radius;
|
||||
}
|
||||
}
|
||||
|
||||
public class Square : Shape
|
||||
{
|
||||
public double Side { get; set; }
|
||||
|
||||
public override double GetArea()
|
||||
{
|
||||
return Side * Side;
|
||||
}
|
||||
}
|
||||
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
Shape shape = new Circle { Radius = 10 };
|
||||
|
||||
// Pattern matching uses isinst
|
||||
if (shape is Circle circle)
|
||||
{
|
||||
return (int)circle.Radius;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -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,46 +53,68 @@ module ExceptionHandling =
|
||||
| Some instructions ->
|
||||
|
||||
// Find all handlers that cover the current PC
|
||||
instructions.ExceptionRegions
|
||||
|> Seq.choose (fun region ->
|
||||
match region with
|
||||
| ExceptionRegion.Catch (typeToken, offset) ->
|
||||
if currentPC >= offset.TryOffset && currentPC < offset.TryOffset + offset.TryLength then
|
||||
// Check if exception type matches
|
||||
if isExceptionAssignableTo exceptionTypeCrate typeToken assemblies then
|
||||
Some (region, false)
|
||||
let handlers =
|
||||
instructions.ExceptionRegions
|
||||
|> Seq.choose (fun region ->
|
||||
match region with
|
||||
| ExceptionRegion.Catch (typeToken, offset) ->
|
||||
if currentPC >= offset.TryOffset && currentPC < offset.TryOffset + offset.TryLength then
|
||||
// Check if exception type matches
|
||||
if isExceptionAssignableTo exceptionTypeCrate typeToken assemblies then
|
||||
Some (region, false, offset.TryOffset, offset.TryLength)
|
||||
else
|
||||
None
|
||||
else
|
||||
None
|
||||
else
|
||||
| ExceptionRegion.Filter (filterOffset, offset) ->
|
||||
if currentPC >= offset.TryOffset && currentPC < offset.TryOffset + offset.TryLength then
|
||||
failwith "TODO: filter needs to be evaluated"
|
||||
else
|
||||
None
|
||||
| ExceptionRegion.Finally offset ->
|
||||
// Don't return finally blocks here - they're handled separately
|
||||
None
|
||||
| ExceptionRegion.Filter (filterOffset, offset) ->
|
||||
if currentPC >= offset.TryOffset && currentPC < offset.TryOffset + offset.TryLength then
|
||||
failwith "TODO: filter needs to be evaluated"
|
||||
else
|
||||
| ExceptionRegion.Fault offset ->
|
||||
// Fault blocks are only executed when propagating exceptions
|
||||
None
|
||||
| ExceptionRegion.Finally offset ->
|
||||
if currentPC >= offset.TryOffset && currentPC < offset.TryOffset + offset.TryLength then
|
||||
Some (region, true)
|
||||
else
|
||||
None
|
||||
| ExceptionRegion.Fault offset ->
|
||||
if currentPC >= offset.TryOffset && currentPC < offset.TryOffset + offset.TryLength then
|
||||
Some (region, true)
|
||||
else
|
||||
None
|
||||
)
|
||||
|> Seq.toList
|
||||
|> fun x ->
|
||||
match x with
|
||||
| [] -> None
|
||||
| [ x ] -> Some x
|
||||
| _ -> failwith "multiple exception regions"
|
||||
)
|
||||
|> 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, offset.TryOffset, offset.TryLength)
|
||||
else
|
||||
None
|
||||
| ExceptionRegion.Fault offset ->
|
||||
if currentPC >= offset.TryOffset && currentPC < offset.TryOffset + offset.TryLength then
|
||||
Some (region, true, offset.TryOffset, offset.TryLength)
|
||||
else
|
||||
None
|
||||
| _ -> None
|
||||
)
|
||||
|> 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