static LambdaExpression ApplyMethodHandler(string functionName, LambdaExpression functionLambda, IFunctionExecutionHandler handler)
        {
            // public static int MyMethod(object arg0, int arg1) { ... }

            // becomes:

            // (the 'handler' object is captured and called mh)
            // public static int MyMethodWrapped(object arg0, int arg1)
            // {
            //    var fhArgs = new FunctionExecutionArgs("MyMethod", new object[] { arg0, arg1});
            //    int result = default(int);
            //    try
            //    {
            //        fh.OnEntry(fhArgs);
            //        if (fhArgs.FlowBehavior == FlowBehavior.Return)
            //        {
            //            result = (int)fhArgs.ReturnValue;
            //        }
            //        else
            //        {
            //             // Inner call
            //             result = MyMethod(arg0, arg1);
            //             fhArgs.ReturnValue = result;
            //             fh.OnSuccess(fhArgs);
            //             result = (int)fhArgs.ReturnValue;
            //        }
            //    }
            //    catch ( Exception ex )
            //    {
            //        fhArgs.Exception = ex;
            //        fh.OnException(fhArgs);
            //        // Makes no sense to me yet - I've removed this FlowBehavior enum value.
            //        // if (fhArgs.FlowBehavior == FlowBehavior.Continue)
            //        // {
            //        //     // Finally will run, but can't change return value
            //        //     // Should we assign result...?
            //        //     // So Default value will be returned....?????
            //        //     fhArgs.Exception = null;
            //        // }
            //        // else
            //        if (fhArgs.FlowBehavior == FlowBehavior.Return)
            //        {
            //            // Clear the Exception and return the ReturnValue instead
            //            // Finally will run, but can't further change return value
            //            fhArgs.Exception = null;
            //            result = (int)fhArgs.ReturnValue;
            //        }
            //        else if (fhArgs.FlowBehavior == FlowBehavior.ThrowException)
            //        {
            //            throw fhArgs.Exception;
            //        }
            //        else // if (fhArgs.FlowBehavior == FlowBehavior.Default || fhArgs.FlowBehavior == FlowBehavior.RethrowException)
            //        {
            //            throw;
            //        }
            //    }
            //    finally
            //    {
            //        fh.OnExit(fhArgs);
            //        // NOTE: fhArgs.ReturnValue is not used again here...!
            //    }
            //
            //    return result;
            //  }
            // }

            // CONSIDER: There are some helpers in .NET to capture the exception context, which would allow us to preserve the stack trace in a fresh throw.

            // Ensure the handler object is captured.
            var mh = Expression.Constant(handler);
            var funcName = Expression.Constant(functionName);

            // Prepare the functionHandlerArgs that will be threaded through the handler,
            // and a bunch of expressions that access various properties on it.
            var fhArgs = Expr.Variable(typeof(FunctionExecutionArgs), "fhArgs");
            var fhArgsReturnValue = SymbolExtensions.GetProperty(fhArgs, (FunctionExecutionArgs mea) => mea.ReturnValue);
            var fhArgsException = SymbolExtensions.GetProperty(fhArgs, (FunctionExecutionArgs mea) => mea.Exception);
            var fhArgsFlowBehaviour = SymbolExtensions.GetProperty(fhArgs, (FunctionExecutionArgs mea) => mea.FlowBehavior);

            // Set up expressions to call the various handler methods.
            // TODO: Later we can determine which of these are actually implemented, and only write out the code needed in the particular case.
            var onEntry = Expr.Call(mh, SymbolExtensions.GetMethodInfo<IFunctionExecutionHandler>(meh => meh.OnEntry(null)), fhArgs);
            var onSuccess = Expr.Call(mh, SymbolExtensions.GetMethodInfo<IFunctionExecutionHandler>(meh => meh.OnSuccess(null)), fhArgs);
            var onException = Expr.Call(mh, SymbolExtensions.GetMethodInfo<IFunctionExecutionHandler>(meh => meh.OnException(null)), fhArgs);
            var onExit = Expr.Call(mh, SymbolExtensions.GetMethodInfo<IFunctionExecutionHandler>(meh => meh.OnExit(null)), fhArgs);

            // Create the new parameters for the wrapper
            var outerParams = functionLambda.Parameters.Select(p => Expr.Parameter(p.Type, p.Name)).ToArray();
            // Create the array of parameter values that will be put into the method handler args.
            var paramsArray = Expr.NewArrayInit(typeof(object), outerParams.Select(p => Expr.Convert(p, typeof(object))));

            // Prepare the result and ex(ception) local variables
            var result = Expr.Variable(functionLambda.ReturnType, "result");
            var ex = Expression.Parameter(typeof(Exception), "ex");

            // A bunch of helper expressions:
            // : new FunctionExecutionArgs(new object[] { arg0, arg1 })
            var fhArgsConstr = typeof(FunctionExecutionArgs).GetConstructor(new[] { typeof(string), typeof(object[]) });
            var newfhArgs = Expr.New(fhArgsConstr, funcName,  paramsArray);
            // : result = (int)fhArgs.ReturnValue
            var resultFromReturnValue = Expr.Assign(result, Expr.Convert(fhArgsReturnValue, functionLambda.ReturnType));
            // : fhArgs.ReturnValue = (object)result
            var returnValueFromResult = Expr.Assign(fhArgsReturnValue, Expr.Convert(result, typeof(object)));
            // : result = function(arg0, arg1)
            var resultFromInnerCall = Expr.Assign(result, Expr.Invoke(functionLambda, outerParams));

            // Build the Lambda wrapper, with the original parameters
            var lambda = Expr.Lambda(
                Expr.Block(new[] { fhArgs, result },
                     Expr.Assign(fhArgs, newfhArgs),
                     Expr.Assign(result, Expr.Default(result.Type)),
                     Expr.TryCatchFinally(
                        Expr.Block(
                            onEntry,
                            Expr.IfThenElse(
                                Expr.Equal(fhArgsFlowBehaviour, Expr.Constant(FlowBehavior.Return)),
                                resultFromReturnValue,
                                Expr.Block(
                                    resultFromInnerCall,
                                    returnValueFromResult,
                                    onSuccess,
                                    resultFromReturnValue))),
                        onExit, // finally
                        Expr.Catch(ex,
                            Expr.Block(
                                Expr.Assign(fhArgsException, ex),
                                onException,
                                Expr.IfThenElse(
                                    Expr.Equal(fhArgsFlowBehaviour, Expr.Constant(FlowBehavior.Return)),
                                    Expr.Block(
                                        Expr.Assign(fhArgsException, Expr.Constant(null, typeof(Exception))),
                                        resultFromReturnValue),
                                    Expr.IfThenElse(
                                        Expr.Equal(fhArgsFlowBehaviour, Expr.Constant(FlowBehavior.ThrowException)),
                                        Expr.Throw(fhArgsException),
                                        Expr.Rethrow()))))
                        ),
                    result),
                functionName,
                outerParams);
            return lambda;
        }
Esempio n. 2
0
        static LambdaExpression ApplyMethodHandler(string functionName, LambdaExpression functionLambda, IFunctionExecutionHandler handler)
        {
            // public static int MyMethod(object arg0, int arg1) { ... }

            // becomes:

            // (the 'handler' object is captured and called mh)
            // public static int MyMethodWrapped(object arg0, int arg1)
            // {
            //    var fhArgs = new FunctionExecutionArgs("MyMethod", new object[] { arg0, arg1});
            //    int result = default(int);
            //    try
            //    {
            //        fh.OnEntry(fhArgs);
            //        if (fhArgs.FlowBehavior == FlowBehavior.Return)
            //        {
            //            result = (int)fhArgs.ReturnValue;
            //        }
            //        else
            //        {
            //             // Inner call
            //             result = MyMethod(arg0, arg1);
            //             fhArgs.ReturnValue = result;
            //             fh.OnSuccess(fhArgs);
            //             result = (int)fhArgs.ReturnValue;
            //        }
            //    }
            //    catch ( Exception ex )
            //    {
            //        fhArgs.Exception = ex;
            //        fh.OnException(fhArgs);
            //        // Makes no sense to me yet - I've removed this FlowBehavior enum value.
            //        // if (fhArgs.FlowBehavior == FlowBehavior.Continue)
            //        // {
            //        //     // Finally will run, but can't change return value
            //        //     // Should we assign result...?
            //        //     // So Default value will be returned....?????
            //        //     fhArgs.Exception = null;
            //        // }
            //        // else
            //        if (fhArgs.FlowBehavior == FlowBehavior.Return)
            //        {
            //            // Clear the Exception and return the ReturnValue instead
            //            // Finally will run, but can't further change return value
            //            fhArgs.Exception = null;
            //            result = (int)fhArgs.ReturnValue;
            //        }
            //        else if (fhArgs.FlowBehavior == FlowBehavior.ThrowException)
            //        {
            //            throw fhArgs.Exception;
            //        }
            //        else // if (fhArgs.FlowBehavior == FlowBehavior.Default || fhArgs.FlowBehavior == FlowBehavior.RethrowException)
            //        {
            //            throw;
            //        }
            //    }
            //    finally
            //    {
            //        fh.OnExit(fhArgs);
            //        // NOTE: fhArgs.ReturnValue is not used again here...!
            //    }
            //
            //    return result;
            //  }
            // }

            // CONSIDER: There are some helpers in .NET to capture the exception context, which would allow us to preserve the stack trace in a fresh throw.

            // Ensure the handler object is captured.
            var mh       = Expression.Constant(handler);
            var funcName = Expression.Constant(functionName);

            // Prepare the functionHandlerArgs that will be threaded through the handler,
            // and a bunch of expressions that access various properties on it.
            var fhArgs              = Expr.Variable(typeof(FunctionExecutionArgs), "fhArgs");
            var fhArgsReturnValue   = SymbolExtensions.GetProperty(fhArgs, (FunctionExecutionArgs mea) => mea.ReturnValue);
            var fhArgsException     = SymbolExtensions.GetProperty(fhArgs, (FunctionExecutionArgs mea) => mea.Exception);
            var fhArgsFlowBehaviour = SymbolExtensions.GetProperty(fhArgs, (FunctionExecutionArgs mea) => mea.FlowBehavior);

            // Set up expressions to call the various handler methods.
            // TODO: Later we can determine which of these are actually implemented, and only write out the code needed in the particular case.
            var onEntry     = Expr.Call(mh, SymbolExtensions.GetMethodInfo <IFunctionExecutionHandler>(meh => meh.OnEntry(null)), fhArgs);
            var onSuccess   = Expr.Call(mh, SymbolExtensions.GetMethodInfo <IFunctionExecutionHandler>(meh => meh.OnSuccess(null)), fhArgs);
            var onException = Expr.Call(mh, SymbolExtensions.GetMethodInfo <IFunctionExecutionHandler>(meh => meh.OnException(null)), fhArgs);
            var onExit      = Expr.Call(mh, SymbolExtensions.GetMethodInfo <IFunctionExecutionHandler>(meh => meh.OnExit(null)), fhArgs);

            // Create the new parameters for the wrapper
            var outerParams = functionLambda.Parameters.Select(p => Expr.Parameter(p.Type, p.Name)).ToArray();
            // Create the array of parameter values that will be put into the method handler args.
            var paramsArray = Expr.NewArrayInit(typeof(object), outerParams.Select(p => Expr.Convert(p, typeof(object))));

            // Prepare the result and ex(ception) local variables
            var result = Expr.Variable(functionLambda.ReturnType, "result");
            var ex     = Expression.Parameter(typeof(Exception), "ex");

            // A bunch of helper expressions:
            // : new FunctionExecutionArgs(new object[] { arg0, arg1 })
            var fhArgsConstr = typeof(FunctionExecutionArgs).GetConstructor(new[] { typeof(string), typeof(object[]) });
            var newfhArgs    = Expr.New(fhArgsConstr, funcName, paramsArray);
            // : result = (int)fhArgs.ReturnValue
            var resultFromReturnValue = Expr.Assign(result, Expr.Convert(fhArgsReturnValue, functionLambda.ReturnType));
            // : fhArgs.ReturnValue = (object)result
            var returnValueFromResult = Expr.Assign(fhArgsReturnValue, Expr.Convert(result, typeof(object)));
            // : result = function(arg0, arg1)
            var resultFromInnerCall = Expr.Assign(result, Expr.Invoke(functionLambda, outerParams));

            // Build the Lambda wrapper, with the original parameters
            var lambda = Expr.Lambda(
                Expr.Block(new[] { fhArgs, result },
                           Expr.Assign(fhArgs, newfhArgs),
                           Expr.Assign(result, Expr.Default(result.Type)),
                           Expr.TryCatchFinally(
                               Expr.Block(
                                   onEntry,
                                   Expr.IfThenElse(
                                       Expr.Equal(fhArgsFlowBehaviour, Expr.Constant(FlowBehavior.Return)),
                                       resultFromReturnValue,
                                       Expr.Block(
                                           resultFromInnerCall,
                                           returnValueFromResult,
                                           onSuccess,
                                           resultFromReturnValue))),
                               onExit, // finally
                               Expr.Catch(ex,
                                          Expr.Block(
                                              Expr.Assign(fhArgsException, ex),
                                              onException,
                                              Expr.IfThenElse(
                                                  Expr.Equal(fhArgsFlowBehaviour, Expr.Constant(FlowBehavior.Return)),
                                                  Expr.Block(
                                                      Expr.Assign(fhArgsException, Expr.Constant(null, typeof(Exception))),
                                                      resultFromReturnValue),
                                                  Expr.IfThenElse(
                                                      Expr.Equal(fhArgsFlowBehaviour, Expr.Constant(FlowBehavior.ThrowException)),
                                                      Expr.Throw(fhArgsException),
                                                      Expr.Rethrow()))))
                               ),
                           result),
                functionName,
                outerParams);

            return(lambda);
        }