From 08a4497ebfd3c4f4e67d7bf6c252d41e543d35b6 Mon Sep 17 00:00:00 2001 From: Patrick Stevens <3138005+Smaug123@users.noreply.github.com> Date: Sat, 6 Sep 2025 16:32:26 +0100 Subject: [PATCH] Recognise more exceptions (#129) --- WoofWare.PawPrint.Domain/TypeInfo.fs | 12 ++ WoofWare.PawPrint/Corelib.fs | 72 +++++++++++ WoofWare.PawPrint/NullaryIlOp.fs | 175 ++++++++++++++++++++------- 3 files changed, 215 insertions(+), 44 deletions(-) diff --git a/WoofWare.PawPrint.Domain/TypeInfo.fs b/WoofWare.PawPrint.Domain/TypeInfo.fs index e61ec4f..3c66086 100644 --- a/WoofWare.PawPrint.Domain/TypeInfo.fs +++ b/WoofWare.PawPrint.Domain/TypeInfo.fs @@ -190,6 +190,18 @@ type BaseClassTypes<'corelib> = TypedReference : TypeInfo IntPtr : TypeInfo UIntPtr : TypeInfo + Exception : TypeInfo + ArithmeticException : TypeInfo + DivideByZeroException : TypeInfo + OverflowException : TypeInfo + StackOverflowException : TypeInfo + TypeLoadException : TypeInfo + IndexOutOfRangeException : TypeInfo + InvalidCastException : TypeInfo + MissingFieldException : TypeInfo + MissingMethodException : TypeInfo + NullReferenceException : TypeInfo + OutOfMemoryException : TypeInfo } [] diff --git a/WoofWare.PawPrint/Corelib.fs b/WoofWare.PawPrint/Corelib.fs index ab5e393..d5f18b5 100644 --- a/WoofWare.PawPrint/Corelib.fs +++ b/WoofWare.PawPrint/Corelib.fs @@ -152,6 +152,66 @@ module Corelib = ) |> Seq.exactlyOne + let exceptionType = + corelib.TypeDefs + |> Seq.choose (fun (KeyValue (_, v)) -> if v.Name = "Exception" then Some v else None) + |> Seq.exactlyOne + + let arithmeticException = + corelib.TypeDefs + |> Seq.choose (fun (KeyValue (_, v)) -> if v.Name = "ArithmeticException" then Some v else None) + |> Seq.exactlyOne + + let divideByZeroException = + corelib.TypeDefs + |> Seq.choose (fun (KeyValue (_, v)) -> if v.Name = "DivideByZeroException" then Some v else None) + |> Seq.exactlyOne + + let overflowException = + corelib.TypeDefs + |> Seq.choose (fun (KeyValue (_, v)) -> if v.Name = "OverflowException" then Some v else None) + |> Seq.exactlyOne + + let stackOverflowException = + corelib.TypeDefs + |> Seq.choose (fun (KeyValue (_, v)) -> if v.Name = "StackOverflowException" then Some v else None) + |> Seq.exactlyOne + + let typeLoadException = + corelib.TypeDefs + |> Seq.choose (fun (KeyValue (_, v)) -> if v.Name = "TypeLoadException" then Some v else None) + |> Seq.exactlyOne + + let indexOutOfRangeException = + corelib.TypeDefs + |> Seq.choose (fun (KeyValue (_, v)) -> if v.Name = "IndexOutOfRangeException" then Some v else None) + |> Seq.exactlyOne + + let invalidCastException = + corelib.TypeDefs + |> Seq.choose (fun (KeyValue (_, v)) -> if v.Name = "InvalidCastException" then Some v else None) + |> Seq.exactlyOne + + let missingFieldException = + corelib.TypeDefs + |> Seq.choose (fun (KeyValue (_, v)) -> if v.Name = "MissingFieldException" then Some v else None) + |> Seq.exactlyOne + + let missingMethodException = + corelib.TypeDefs + |> Seq.choose (fun (KeyValue (_, v)) -> if v.Name = "MissingMethodException" then Some v else None) + |> Seq.exactlyOne + + let nullReferenceException = + corelib.TypeDefs + |> Seq.choose (fun (KeyValue (_, v)) -> if v.Name = "NullReferenceException" then Some v else None) + |> Seq.exactlyOne + + let outOfMemoryException = + corelib.TypeDefs + |> Seq.choose (fun (KeyValue (_, v)) -> if v.Name = "OutOfMemoryException" then Some v else None) + |> Seq.exactlyOne + { Corelib = corelib String = stringType @@ -182,6 +242,18 @@ module Corelib = TypedReference = typedReferenceType IntPtr = intPtrType UIntPtr = uintPtrType + Exception = exceptionType + ArithmeticException = arithmeticException + DivideByZeroException = divideByZeroException + OverflowException = overflowException + StackOverflowException = stackOverflowException + TypeLoadException = typeLoadException + IndexOutOfRangeException = indexOutOfRangeException + InvalidCastException = invalidCastException + MissingFieldException = missingFieldException + MissingMethodException = missingMethodException + NullReferenceException = nullReferenceException + OutOfMemoryException = outOfMemoryException } let concretizeAll diff --git a/WoofWare.PawPrint/NullaryIlOp.fs b/WoofWare.PawPrint/NullaryIlOp.fs index d00a83b..76b0ec1 100644 --- a/WoofWare.PawPrint/NullaryIlOp.fs +++ b/WoofWare.PawPrint/NullaryIlOp.fs @@ -86,43 +86,28 @@ module NullaryIlOp = | ManagedPointerSource.Argument (sourceThread, methodFrame, whichVar) -> failwith "unexpected - can we really write to an argument?" | ManagedPointerSource.LocalVariable (sourceThread, methodFrame, whichVar) -> - { state with - ThreadState = - state.ThreadState - |> Map.change - sourceThread - (fun state -> - match state with - | None -> failwith "tried to store in local variables of nonexistent stack frame" - | Some state -> - let frame = state.MethodStates.[methodFrame] - - let frame = - { frame with - LocalVariables = - frame.LocalVariables.SetItem ( - int whichVar, - EvalStackValue.toCliTypeCoerced varType valueToStore - ) - } - - { state with - MethodStates = state.MethodStates.SetItem (methodFrame, frame) - } - |> Some - ) - } + state + |> IlMachineState.setLocalVariable + sourceThread + methodFrame + whichVar + (EvalStackValue.toCliTypeCoerced varType valueToStore) | ManagedPointerSource.Heap managedHeapAddress -> failwith "todo" | ManagedPointerSource.ArrayIndex _ -> failwith "todo" - | ManagedPointerSource.Field (managedPointerSource, fieldName) -> failwith "todo" + | ManagedPointerSource.Field (managedPointerSource, fieldName) -> + state + |> IlMachineState.setFieldValue + managedPointerSource + (EvalStackValue.toCliTypeCoerced varType valueToStore) + fieldName | EvalStackValue.ObjectRef managedHeapAddress -> failwith "todo" - let internal ldElem + let internal getArrayElt (index : EvalStackValue) (arr : EvalStackValue) (currentThread : ThreadId) (state : IlMachineState) - : ExecutionResult + : CliType = let index = match index with @@ -143,14 +128,7 @@ module NullaryIlOp = | EvalStackValue.ManagedPointer ManagedPointerSource.Null -> failwith "TODO: throw NRE" | _ -> failwith $"Invalid array: %O{arr}" - let value = IlMachineState.getArrayValue arrAddr index state - - let state = - state - |> IlMachineState.pushToEvalStack value currentThread - |> IlMachineState.advanceProgramCounter currentThread - - ExecutionResult.Stepped (state, WhatWeDid.Executed) + IlMachineState.getArrayValue arrAddr index state let internal stElem (targetCliTypeZero : CliType) @@ -547,7 +525,33 @@ module NullaryIlOp = |> IlMachineState.advanceProgramCounter currentThread (state, WhatWeDid.Executed) |> ExecutionResult.Stepped - | Shr_un -> failwith "TODO: Shr_un unimplemented" + | Shr_un -> + let shift, state = IlMachineState.popEvalStack currentThread state + let number, state = IlMachineState.popEvalStack currentThread state + + let shift = + match shift with + | EvalStackValue.Int32 i -> i + | EvalStackValue.NativeInt (NativeIntSource.Verbatim i) -> int i + | _ -> failwith $"Not allowed shift of {shift}" + + let result = + // See table III.6 + match number with + | EvalStackValue.Int32 i -> uint32 i >>> shift |> int32 |> EvalStackValue.Int32 + | EvalStackValue.Int64 i -> uint64 i >>> shift |> int64 |> EvalStackValue.Int64 + | EvalStackValue.NativeInt (NativeIntSource.Verbatim i) -> + (uint64 i >>> shift |> int64) + |> NativeIntSource.Verbatim + |> EvalStackValue.NativeInt + | _ -> failwith $"Not allowed to shift {number}" + + let state = + state + |> IlMachineState.pushToEvalStack' result currentThread + |> IlMachineState.advanceProgramCounter currentThread + + (state, WhatWeDid.Executed) |> ExecutionResult.Stepped | Shl -> let shift, state = IlMachineState.popEvalStack currentThread state let number, state = IlMachineState.popEvalStack currentThread state @@ -1040,14 +1044,85 @@ module NullaryIlOp = let state = state |> IlMachineState.advanceProgramCounter currentThread (state, WhatWeDid.Executed) |> ExecutionResult.Stepped - | Ldelem_i -> failwith "TODO: Ldelem_i unimplemented" - | Ldelem_i1 -> failwith "TODO: Ldelem_i1 unimplemented" + | Ldelem_i -> + let index, state = IlMachineState.popEvalStack currentThread state + let arr, state = IlMachineState.popEvalStack currentThread state + + let value = getArrayElt index arr currentThread state + + match value with + | CliType.Numeric (CliNumericType.NativeInt _) -> () + | _ -> failwith "expected native int in Ldelem.i" + + let state = + state + |> IlMachineState.pushToEvalStack value currentThread + |> IlMachineState.advanceProgramCounter currentThread + + ExecutionResult.Stepped (state, WhatWeDid.Executed) + | Ldelem_i1 -> + let index, state = IlMachineState.popEvalStack currentThread state + let arr, state = IlMachineState.popEvalStack currentThread state + + let value = getArrayElt index arr currentThread state + + failwith "TODO: we got back an int8; turn it into int32" + + let state = + state + |> IlMachineState.pushToEvalStack value currentThread + |> IlMachineState.advanceProgramCounter currentThread + + ExecutionResult.Stepped (state, WhatWeDid.Executed) | Ldelem_u1 -> failwith "TODO: Ldelem_u1 unimplemented" - | Ldelem_i2 -> failwith "TODO: Ldelem_i2 unimplemented" + | Ldelem_i2 -> + let index, state = IlMachineState.popEvalStack currentThread state + let arr, state = IlMachineState.popEvalStack currentThread state + + let value = getArrayElt index arr currentThread state + + failwith "TODO: we got back an int16; turn it into int32" + + let state = + state + |> IlMachineState.pushToEvalStack value currentThread + |> IlMachineState.advanceProgramCounter currentThread + + ExecutionResult.Stepped (state, WhatWeDid.Executed) | Ldelem_u2 -> failwith "TODO: Ldelem_u2 unimplemented" - | Ldelem_i4 -> failwith "TODO: Ldelem_i4 unimplemented" + | Ldelem_i4 -> + let index, state = IlMachineState.popEvalStack currentThread state + let arr, state = IlMachineState.popEvalStack currentThread state + + let value = getArrayElt index arr currentThread state + + match value with + | CliType.Numeric (CliNumericType.Int32 _) -> () + | _ -> failwith "expected int32 in Ldelem.i4" + + let state = + state + |> IlMachineState.pushToEvalStack value currentThread + |> IlMachineState.advanceProgramCounter currentThread + + ExecutionResult.Stepped (state, WhatWeDid.Executed) | Ldelem_u4 -> failwith "TODO: Ldelem_u4 unimplemented" - | Ldelem_i8 -> failwith "TODO: Ldelem_i8 unimplemented" + | Ldelem_i8 -> + let index, state = IlMachineState.popEvalStack currentThread state + let arr, state = IlMachineState.popEvalStack currentThread state + + let value = getArrayElt index arr currentThread state + + match value with + | CliType.Numeric (CliNumericType.Int64 _) -> () + | _ -> failwith "expected int64 in Ldelem.i8" + + let state = + state + |> IlMachineState.pushToEvalStack value currentThread + |> IlMachineState.advanceProgramCounter currentThread + + ExecutionResult.Stepped (state, WhatWeDid.Executed) | Ldelem_u8 -> failwith "TODO: Ldelem_u8 unimplemented" | Ldelem_r4 -> failwith "TODO: Ldelem_r4 unimplemented" | Ldelem_r8 -> failwith "TODO: Ldelem_r8 unimplemented" @@ -1055,7 +1130,19 @@ module NullaryIlOp = let index, state = IlMachineState.popEvalStack currentThread state let arr, state = IlMachineState.popEvalStack currentThread state - ldElem index arr currentThread state + let value = getArrayElt index arr currentThread state + + match value with + | CliType.ObjectRef _ + | CliType.RuntimePointer _ -> () + | _ -> failwith "expected object reference in Ldelem.ref" + + let state = + state + |> IlMachineState.pushToEvalStack value currentThread + |> IlMachineState.advanceProgramCounter currentThread + + ExecutionResult.Stepped (state, WhatWeDid.Executed) | Stelem_i -> let value, state = IlMachineState.popEvalStack currentThread state let index, state = IlMachineState.popEvalStack currentThread state