5 people like it.

Simple exception analysis

A simple script to list the exceptions that can be thrown during the method invocation (both caught and uncaught).

Simple exception analysis

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
32: 
33: 
34: 
35: 
36: 
37: 
38: 
39: 
40: 
41: 
42: 
43: 
44: 
45: 
46: 
47: 
48: 
49: 
50: 
51: 
52: 
53: 
54: 
55: 
56: 
57: 
58: 
59: 
60: 
61: 
62: 
63: 
64: 
65: 
66: 
67: 
68: 
69: 
70: 
71: 
72: 
73: 
74: 
75: 
76: 
77: 
78: 
open Mono.Reflection
    
/// check for the throw opcode preceded with newobj 
let getThrownException (instr: Instruction) =
    let prev = instr.Previous
    if instr.OpCode = OpCodes.Throw && prev <> null && prev.OpCode = OpCodes.Newobj then 
        let newType = (prev.Operand :?> ConstructorInfo).ReflectedType
        if typeof<System.Exception>.IsAssignableFrom newType then
            Some (instr, newType.FullName)
        else None
    else None


let inline instructionInfo (m: MethodBase) (i, t) comment = 
    sprintf "%s->%s: %A%s" m.ReflectedType.FullName m.Name t comment

let inline hasCallOpcode (i: Instruction) =
    i.OpCode = OpCodes.Call || i.OpCode = OpCodes.Callvirt || i.OpCode = OpCodes.Calli

/// add some additional information about the exceptions
/// e.g. the value of message parameter for the standard ArgumentException 
(get exception resource)

let addComment (i: Instruction) =
    let comment = 
        match i.Operand with
        | :? MethodBase as mi when i.OpCode=OpCodes.Call && mi.Name="ThrowArgumentException" 
                                                         && mi.GetParameters().Length>0 ->
            let msg = tryGetExceptionResource i.Previous.Operand 
            if Option.isSome msg then
                sprintf " (param: %A)" msg.Value 
            else ""
        | _ -> ""
    i, comment
    
/// add exceptions to dictionary
let inline (<~) (dict: Dictionary<_, HashSet<_>>) (m, items) = (...)

/// perform analysis for a method with given detalization parameters 
let analyzeExceptions (m: MethodBase) (detalization, maxDepth) =
    printfn ">> Analyze %s (%s):" m.Name m.ReflectedType.FullName
    let exnsFound = Dictionary<_,HashSet<_>>()

    let rec check d (m: MethodBase) comment = 
        if m = null || m.GetMethodBody() = null then HashSet<_>()
        else
            let tab = String.replicate d " "
            let inline printDetails() = (...)

            if exnsFound.ContainsKey m then printDetails()
            else
                let instructions = m.GetInstructions()
                // exceptions in the method body
                let exns = 
                    instructions 
                    |> Seq.map getThrownException 
                    |> Seq.filter Option.isSome 
                    |> Seq.map (fun e -> instructionInfo m e.Value comment)
            
                exnsFound <~ (m, exns)
                if d < detalization - 1 then printDetails()     

                // exceptions in inner calls
                if d < maxDepth then
                    let innerExceptions = 
                        instructions 
                        |> Seq.filter hasCallOpcode
                        |> Seq.map addComment
                        |> Seq.collect (fun (i, c) -> check (d+1) (i.Operand :?> MethodBase) c)
                    exnsFound <~ (m, innerExceptions)
                if d = detalization - 1 then printDetails()
            exnsFound.[m]

    check 0 m "" |> (fun i -> printfn "Summary:"; i) |> Seq.iter (printfn "  %s")

(Example: get a couple of methods to analyze)  
analyzeExceptions methods.["ToDictionary"] (2, 5) (output)  
analyzeExceptions methods.["ThrowException"] (0, 5) (output)
namespace Mono
namespace Mono.Reflection
val getThrownException : instr:Instruction -> (Instruction * string) option

Full name: Script.getThrownException


 check for the throw opcode preceded with newobj
val instr : Instruction
type Instruction =
  member Next : Instruction with get, set
  member Offset : int
  member OpCode : OpCode
  member Operand : obj with get, set
  member Previous : Instruction with get, set
  member Size : int
  member ToString : unit -> string

Full name: Mono.Reflection.Instruction
val prev : Instruction
property Instruction.Previous: Instruction
property Instruction.OpCode: OpCode
type OpCodes =
  static val Nop : OpCode
  static val Break : OpCode
  static val Ldarg_0 : OpCode
  static val Ldarg_1 : OpCode
  static val Ldarg_2 : OpCode
  static val Ldarg_3 : OpCode
  static val Ldloc_0 : OpCode
  static val Ldloc_1 : OpCode
  static val Ldloc_2 : OpCode
  static val Ldloc_3 : OpCode
  ...

Full name: System.Reflection.Emit.OpCodes
field OpCodes.Throw
field OpCodes.Newobj
val newType : System.Type
property Instruction.Operand: obj
type ConstructorInfo =
  inherit MethodBase
  member Equals : obj:obj -> bool
  member GetHashCode : unit -> int
  member Invoke : parameters:obj[] -> obj + 1 overload
  member MemberType : MemberTypes
  static val ConstructorName : string
  static val TypeConstructorName : string

Full name: System.Reflection.ConstructorInfo
val typeof<'T> : System.Type

Full name: Microsoft.FSharp.Core.Operators.typeof
namespace System
Multiple items
type Exception =
  new : unit -> Exception + 2 overloads
  member Data : IDictionary
  member GetBaseException : unit -> Exception
  member GetObjectData : info:SerializationInfo * context:StreamingContext -> unit
  member GetType : unit -> Type
  member HelpLink : string with get, set
  member InnerException : Exception
  member Message : string
  member Source : string with get, set
  member StackTrace : string
  ...

Full name: System.Exception

--------------------
System.Exception() : unit
System.Exception(message: string) : unit
System.Exception(message: string, innerException: exn) : unit
union case Option.Some: Value: 'T -> Option<'T>
property System.Type.FullName: string
union case Option.None: Option<'T>
val instructionInfo : m:MethodBase -> i:'a * t:'b -> comment:string -> string

Full name: Script.instructionInfo
val m : MethodBase
type MethodBase =
  inherit MemberInfo
  member Attributes : MethodAttributes
  member CallingConvention : CallingConventions
  member ContainsGenericParameters : bool
  member Equals : obj:obj -> bool
  member GetGenericArguments : unit -> Type[]
  member GetHashCode : unit -> int
  member GetMethodBody : unit -> MethodBody
  member GetMethodImplementationFlags : unit -> MethodImplAttributes
  member GetParameters : unit -> ParameterInfo[]
  member Invoke : obj:obj * parameters:obj[] -> obj + 1 overload
  ...

Full name: System.Reflection.MethodBase
val i : 'a
val t : 'b
val comment : string
val sprintf : format:Printf.StringFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.sprintf
property MemberInfo.ReflectedType: System.Type
property MemberInfo.Name: string
val hasCallOpcode : i:Instruction -> bool

Full name: Script.hasCallOpcode
val i : Instruction
field OpCodes.Call
field OpCodes.Callvirt
field OpCodes.Calli
let types = System.Reflection.Assembly.GetAssembly(typeof<System.ArgumentException>).GetTypes()
let tryGetExceptionResource (prm: obj) =
    types
    |> Seq.tryFind (fun t -> t.FullName = "System.ExceptionResource")
    |> Option.bind (fun e ->Some (System.Enum.ToObject(e, prm)))
val addComment : i:Instruction -> Instruction * string

Full name: Script.addComment
val mi : MethodBase
MethodBase.GetParameters() : ParameterInfo []
val msg : obj option
val tryGetExceptionResource : prm:obj -> obj option

Full name: Script.tryGetExceptionResource
module Option

from Microsoft.FSharp.Core
val isSome : option:'T option -> bool

Full name: Microsoft.FSharp.Core.Option.isSome
property Option.Value: obj
val dict : Dictionary<'a,HashSet<'b>>
Multiple items
type Dictionary<'TKey,'TValue> =
  new : unit -> Dictionary<'TKey, 'TValue> + 5 overloads
  member Add : key:'TKey * value:'TValue -> unit
  member Clear : unit -> unit
  member Comparer : IEqualityComparer<'TKey>
  member ContainsKey : key:'TKey -> bool
  member ContainsValue : value:'TValue -> bool
  member Count : int
  member GetEnumerator : unit -> Enumerator<'TKey, 'TValue>
  member GetObjectData : info:SerializationInfo * context:StreamingContext -> unit
  member Item : 'TKey -> 'TValue with get, set
  ...
  nested type Enumerator
  nested type KeyCollection
  nested type ValueCollection

Full name: System.Collections.Generic.Dictionary<_,_>

--------------------
Dictionary() : unit
Dictionary(capacity: int) : unit
Dictionary(comparer: IEqualityComparer<'TKey>) : unit
Dictionary(dictionary: IDictionary<'TKey,'TValue>) : unit
Dictionary(capacity: int, comparer: IEqualityComparer<'TKey>) : unit
Dictionary(dictionary: IDictionary<'TKey,'TValue>, comparer: IEqualityComparer<'TKey>) : unit
Multiple items
type HashSet<'T> =
  new : unit -> HashSet<'T> + 3 overloads
  member Add : item:'T -> bool
  member Clear : unit -> unit
  member Comparer : IEqualityComparer<'T>
  member Contains : item:'T -> bool
  member CopyTo : array:'T[] -> unit + 2 overloads
  member Count : int
  member ExceptWith : other:IEnumerable<'T> -> unit
  member GetEnumerator : unit -> Enumerator<'T>
  member GetObjectData : info:SerializationInfo * context:StreamingContext -> unit
  ...
  nested type Enumerator

Full name: System.Collections.Generic.HashSet<_>

--------------------
HashSet() : unit
HashSet(comparer: IEqualityComparer<'T>) : unit
HashSet(collection: IEnumerable<'T>) : unit
HashSet(collection: IEnumerable<'T>, comparer: IEqualityComparer<'T>) : unit
val m : 'a
val items : seq<'b>
let set = if dict.ContainsKey m then dict.[m]
              else
                let s = HashSet<_>() in dict.Add(m, s); s
    Seq.iter (set.Add >> ignore) items
val analyzeExceptions : m:MethodBase -> detalization:int * maxDepth:int -> unit

Full name: Script.analyzeExceptions


 perform analysis for a method with given detalization parameters
val detalization : int
val maxDepth : int
val printfn : format:Printf.TextWriterFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
val exnsFound : Dictionary<MethodBase,HashSet<string>>
val check : (int -> MethodBase -> string -> HashSet<string>)
val d : int
MethodBase.GetMethodBody() : MethodBody
val tab : string
module String

from Microsoft.FSharp.Core
val replicate : count:int -> str:string -> string

Full name: Microsoft.FSharp.Core.String.replicate
val printDetails : (unit -> unit)
printfn "%s>> %s (%s)" tab m.Name m.ReflectedType.FullName
                let detExns = exnsFound.[m]
                if detExns.Count > 0 then detExns |> Seq.iter (printfn "\t%s%s" tab)
Dictionary.ContainsKey(key: MethodBase) : bool
val instructions : IList<Instruction>
(extension) MethodBase.GetInstructions() : IList<Instruction>
val exns : seq<string>
module Seq

from Microsoft.FSharp.Collections
val map : mapping:('T -> 'U) -> source:seq<'T> -> seq<'U>

Full name: Microsoft.FSharp.Collections.Seq.map
val filter : predicate:('T -> bool) -> source:seq<'T> -> seq<'T>

Full name: Microsoft.FSharp.Collections.Seq.filter
val e : (Instruction * string) option
property Option.Value: Instruction * string
val innerExceptions : seq<string>
val collect : mapping:('T -> #seq<'U>) -> source:seq<'T> -> seq<'U>

Full name: Microsoft.FSharp.Collections.Seq.collect
val c : string
val i : HashSet<string>
val iter : action:('T -> unit) -> source:seq<'T> -> unit

Full name: Microsoft.FSharp.Collections.Seq.iter
open System.Linq
type T() =
    let dict = Dictionary<_,_>()
    member x.ToDictionary (arr: _[]) = arr.ToDictionary(id, string)
    member x.ThrowException() = failwith "exception here"

let methods = typeof<T>.GetMethods() |> Seq.filter (fun m -> m.DeclaringType = typeof<T> && m.GetMethodBody() <> null)
                                     |> Seq.map (fun m -> m.Name, m) |> Map.ofSeq
val methods : Map<string,MethodInfo>

Full name: Script.methods
>> Analyze ToDictionary (FSI_0003+T):
>> ToDictionary (FSI_0003+T)
   >> ArgumentNull (System.Linq.Error)
   >> ArgumentNull (System.Linq.Error)
 >> ToDictionary (System.Linq.Enumerable)
System.ThrowHelper->ThrowArgumentNullException: "System.ArgumentNullException"
System.ThrowHelper->ThrowArgumentException: "System.ArgumentException" (param: Argument_AddingDuplicate)
Summary:
  System.ThrowHelper->ThrowArgumentNullException: "System.ArgumentNullException"
  System.ThrowHelper->ThrowArgumentException: "System.ArgumentException" (param: Argument_AddingDuplicate)
>> Analyze ThrowException (FSI_0003+T):
Summary:
  FSI_0003+T->ThrowException: "System.Exception"
Raw view Test code New version

More information

Link:http://fssnip.net/dx
Posted:11 years ago
Author:Natallie Baikevich
Tags: reflection , exceptions