public async Task Generate_EndToEnd()
        {
            // construct our TimerTrigger attribute ([TimerTrigger("00:00:02", RunOnStartup = true)])
            Collection<ParameterDescriptor> parameters = new Collection<ParameterDescriptor>();
            ParameterDescriptor parameter = new ParameterDescriptor("timerInfo", typeof(TimerInfo));
            ConstructorInfo ctorInfo = typeof(TimerTriggerAttribute).GetConstructor(new Type[] { typeof(string) });
            PropertyInfo runOnStartupProperty = typeof(TimerTriggerAttribute).GetProperty("RunOnStartup");
            CustomAttributeBuilder attributeBuilder = new CustomAttributeBuilder(
                ctorInfo,
                new object[] { "00:00:02" },
                new PropertyInfo[] { runOnStartupProperty },
                new object[] { true });
            parameter.CustomAttributes.Add(attributeBuilder);
            parameters.Add(parameter);

            // create the FunctionDefinition
            FunctionMetadata metadata = new FunctionMetadata();
            TestInvoker invoker = new TestInvoker();
            FunctionDescriptor function = new FunctionDescriptor("TimerFunction", invoker, metadata, parameters);
            Collection<FunctionDescriptor> functions = new Collection<FunctionDescriptor>();
            functions.Add(function);

            // Get the Type Attributes (in this case, a TimeoutAttribute)
            ScriptHostConfiguration scriptConfig = new ScriptHostConfiguration();
            scriptConfig.FunctionTimeout = TimeSpan.FromMinutes(5);
            Collection<CustomAttributeBuilder> typeAttributes = ScriptHost.CreateTypeAttributes(scriptConfig);

            // generate the Type
            Type functionType = FunctionGenerator.Generate("TestScriptHost", "TestFunctions", typeAttributes, functions);

            // verify the generated function
            MethodInfo method = functionType.GetMethod("TimerFunction");
            TimeoutAttribute timeoutAttribute = (TimeoutAttribute)functionType.GetCustomAttributes().Single();
            Assert.Equal(TimeSpan.FromMinutes(5), timeoutAttribute.Timeout);
            Assert.True(timeoutAttribute.ThrowOnTimeout);
            Assert.True(timeoutAttribute.TimeoutWhileDebugging);
            ParameterInfo triggerParameter = method.GetParameters()[0];
            TimerTriggerAttribute triggerAttribute = triggerParameter.GetCustomAttribute<TimerTriggerAttribute>();
            Assert.NotNull(triggerAttribute);

            // start the JobHost which will start running the timer function
            JobHostConfiguration config = new JobHostConfiguration()
            {
                TypeLocator = new TypeLocator(functionType)
            };
            config.UseTimers();
            JobHost host = new JobHost(config);

            await host.StartAsync();
            await Task.Delay(3000);
            await host.StopAsync();

            // verify our custom invoker was called
            Assert.True(invoker.InvokeCount >= 2);
        }
Exemple #2
0
        protected override Collection <ParameterDescriptor> GetFunctionParameters(IFunctionInvoker functionInvoker, FunctionMetadata functionMetadata,
                                                                                  BindingMetadata triggerMetadata, Collection <CustomAttributeBuilder> methodAttributes, Collection <FunctionBinding> inputBindings, Collection <FunctionBinding> outputBindings)
        {
            if (functionInvoker == null)
            {
                throw new ArgumentNullException("functionInvoker");
            }
            if (functionMetadata == null)
            {
                throw new ArgumentNullException("functionMetadata");
            }
            if (triggerMetadata == null)
            {
                throw new ArgumentNullException("triggerMetadata");
            }
            if (methodAttributes == null)
            {
                throw new ArgumentNullException("methodAttributes");
            }

            var dotNetInvoker = functionInvoker as DotNetFunctionInvoker;

            if (dotNetInvoker == null)
            {
                throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Expected invoker of type '{0}' but received '{1}'", typeof(DotNetFunctionInvoker).Name, functionInvoker.GetType().Name));
            }

            try
            {
                ApplyMethodLevelAttributes(functionMetadata, triggerMetadata, methodAttributes);

                MethodInfo      functionTarget = dotNetInvoker.GetFunctionTargetAsync().Result;
                ParameterInfo[] parameters     = functionTarget.GetParameters();
                Collection <ParameterDescriptor> descriptors = new Collection <ParameterDescriptor>();
                IEnumerable <FunctionBinding>    bindings    = inputBindings.Union(outputBindings);
                ParameterDescriptor descriptor = null;
                foreach (var parameter in parameters)
                {
                    // Is it the trigger parameter?
                    if (string.Compare(parameter.Name, triggerMetadata.Name, StringComparison.Ordinal) == 0)
                    {
                        ParameterDescriptor triggerParameter = CreateTriggerParameter(triggerMetadata, parameter.ParameterType);
                        descriptors.Add(triggerParameter);
                    }
                    else
                    {
                        Type parameterType    = parameter.ParameterType;
                        bool parameterIsByRef = parameterType.IsByRef;
                        if (parameterIsByRef)
                        {
                            parameterType = parameterType.GetElementType();
                        }

                        descriptor = new ParameterDescriptor(parameter.Name, parameter.ParameterType);
                        var binding = bindings.FirstOrDefault(b => string.Compare(b.Metadata.Name, parameter.Name, StringComparison.Ordinal) == 0);
                        if (binding != null)
                        {
                            Collection <CustomAttributeBuilder> customAttributes = binding.GetCustomAttributes(parameter.ParameterType);
                            if (customAttributes != null)
                            {
                                foreach (var customAttribute in customAttributes)
                                {
                                    descriptor.CustomAttributes.Add(customAttribute);
                                }
                            }
                        }

                        // In the C# programming model, IsOut is set for out parameters
                        // In the F# programming model, neither IsOut nor IsIn are set for byref parameters (which are used as out parameters).
                        //   Justification for this cariation of the programming model is that declaring 'out' parameters is (deliberately)
                        //   awkward in F#, they require opening System.Runtime.InteropServices and adding the [<Out>] attribute, and using
                        //   a byref parameter. In contrast declaring a byref parameter alone (neither labelled In nor Out) is simple enough.
                        if (parameter.IsOut || (functionMetadata.ScriptType == ScriptType.FSharp && parameterIsByRef && !parameter.IsIn))
                        {
                            descriptor.Attributes |= ParameterAttributes.Out;
                        }

                        descriptors.Add(descriptor);
                    }
                }

                // Add any additional required System parameters (if they haven't already been defined by the user)
                if (!descriptors.Any(p => p.Type == typeof(ExecutionContext)))
                {
                    // Add ExecutionContext to provide access to InvocationId, etc.
                    descriptors.Add(new ParameterDescriptor(ScriptConstants.SystemExecutionContextParameterName, typeof(ExecutionContext)));
                }

                // If we have an HTTP trigger binding but no parameter binds to the raw HttpRequestMessage,
                // add it as a system parameter so it is accessible later in the pipeline.
                if (string.Compare(triggerMetadata.Type, "httptrigger", StringComparison.OrdinalIgnoreCase) == 0 &&
                    !descriptors.Any(p => p.Type == typeof(HttpRequestMessage)))
                {
                    descriptors.Add(new ParameterDescriptor(ScriptConstants.SystemTriggerParameterName, typeof(HttpRequestMessage)));
                }

                if (TryCreateReturnValueParameterDescriptor(functionTarget.ReturnType, bindings, out descriptor))
                {
                    // If a return value binding has been specified, set up an output
                    // binding to map it to. By convention, this is set up as the last
                    // parameter.
                    descriptors.Add(descriptor);
                }

                return(descriptors);
            }
            catch (AggregateException exc)
            {
                if (!(exc.InnerException is CompilationErrorException))
                {
                    throw;
                }
            }
            catch (CompilationErrorException)
            {
            }

            // We were unable to compile the function to get its signature,
            // setup the descriptor with the default parameters
            methodAttributes.Clear();
            return(base.GetFunctionParameters(functionInvoker, functionMetadata, triggerMetadata, methodAttributes, inputBindings, outputBindings));
        }
Exemple #3
0
        internal static bool TryCreateReturnValueParameterDescriptor(Type functionReturnType, IEnumerable <FunctionBinding> bindings, out ParameterDescriptor descriptor)
        {
            descriptor = null;

            var returnBinding = bindings.SingleOrDefault(p => p.Metadata.IsReturn);

            if (returnBinding == null)
            {
                return(false);
            }
            var resultBinding = returnBinding as IResultProcessingBinding;

            if (resultBinding != null)
            {
                if (resultBinding.CanProcessResult(true))
                {
                    // The trigger binding (ie, httpTrigger) will handle the return.
                    return(false);
                }
            }

            if (typeof(Task).IsAssignableFrom(functionReturnType))
            {
                if (!(functionReturnType.IsGenericType && functionReturnType.GetGenericTypeDefinition() == typeof(Task <>)))
                {
                    throw new InvalidOperationException($"{ScriptConstants.SystemReturnParameterBindingName} cannot be bound to return type {functionReturnType.Name}.");
                }
                functionReturnType = functionReturnType.GetGenericArguments()[0];
            }

            var byRefType = functionReturnType.MakeByRefType();

            descriptor             = new ParameterDescriptor(ScriptConstants.SystemReturnParameterName, byRefType);
            descriptor.Attributes |= ParameterAttributes.Out;

            Collection <CustomAttributeBuilder> customAttributes = returnBinding.GetCustomAttributes(byRefType);

            if (customAttributes != null)
            {
                foreach (var customAttribute in customAttributes)
                {
                    descriptor.CustomAttributes.Add(customAttribute);
                }
            }

            return(true);
        }
        protected override Collection<ParameterDescriptor> GetFunctionParameters(IFunctionInvoker functionInvoker, FunctionMetadata functionMetadata,
            BindingMetadata triggerMetadata, Collection<CustomAttributeBuilder> methodAttributes, Collection<FunctionBinding> inputBindings, Collection<FunctionBinding> outputBindings)
        {
            if (functionInvoker == null)
            {
                throw new ArgumentNullException("functionInvoker");
            }
            if (functionMetadata == null)
            {
                throw new ArgumentNullException("functionMetadata");
            }
            if (triggerMetadata == null)
            {
                throw new ArgumentNullException("triggerMetadata");
            }
            if (methodAttributes == null)
            {
                throw new ArgumentNullException("methodAttributes");
            }

            var csharpInvoker = functionInvoker as CSharpFunctionInvoker;
            if (csharpInvoker == null)
            {
                throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Expected invoker of type '{0}' but received '{1}'", typeof(CSharpFunctionInvoker).Name, functionInvoker.GetType().Name));
            }

            try
            {
                ApplyMethodLevelAttributes(functionMetadata, triggerMetadata, methodAttributes);

                MethodInfo functionTarget = csharpInvoker.GetFunctionTargetAsync().Result;
                ParameterInfo[] parameters = functionTarget.GetParameters();
                Collection<ParameterDescriptor> descriptors = new Collection<ParameterDescriptor>();
                IEnumerable<FunctionBinding> bindings = inputBindings.Union(outputBindings);
                bool addHttpRequestSystemParameter = false;
                foreach (var parameter in parameters)
                {
                    // Is it the trigger parameter?
                    if (string.Compare(parameter.Name, triggerMetadata.Name, StringComparison.Ordinal) == 0)
                    {
                        descriptors.Add(CreateTriggerParameterDescriptor(parameter, triggerMetadata));

                        if (triggerMetadata.Type == BindingType.HttpTrigger && 
                            parameter.ParameterType != typeof(HttpRequestMessage))
                        {
                            addHttpRequestSystemParameter = true;
                        }
                    }
                    else
                    {
                        Type parameterType = parameter.ParameterType;
                        if (parameterType.IsByRef)
                        {
                            parameterType = parameterType.GetElementType();
                        }

                        var descriptor = new ParameterDescriptor(parameter.Name, parameter.ParameterType);
                        var binding = bindings.FirstOrDefault(b => string.Compare(b.Metadata.Name, parameter.Name, StringComparison.Ordinal) == 0);
                        if (binding != null)
                        {
                            Collection<CustomAttributeBuilder> customAttributes = binding.GetCustomAttributes();
                            if (customAttributes != null)
                            {
                                foreach (var customAttribute in customAttributes)
                                {
                                    descriptor.CustomAttributes.Add(customAttribute);
                                }
                            }
                        }

                        if (parameter.IsOut)
                        {
                            descriptor.Attributes |= ParameterAttributes.Out;
                        }

                        descriptors.Add(descriptor);
                    }
                }

                // Add any additional common System parameters
                // Add ExecutionContext to provide access to InvocationId, etc.
                descriptors.Add(new ParameterDescriptor("context", typeof(ExecutionContext)));
                
                // If we have an HTTP trigger binding but we're not binding
                // to the HttpRequestMessage, require it as a system parameter
                if (addHttpRequestSystemParameter)
                {
                    descriptors.Add(new ParameterDescriptor(ScriptConstants.DefaultSystemTriggerParameterName, typeof(HttpRequestMessage)));
                }

                return descriptors;
            }
            catch (AggregateException exc)
            {
                if (!(exc.InnerException is CompilationErrorException))
                {
                    throw;
                }
            }
            catch (CompilationErrorException)
            {
            }

            // We were unable to compile the function to get its signature,
            // setup the descriptor with the default parameters
            return base.GetFunctionParameters(functionInvoker, functionMetadata, triggerMetadata, methodAttributes, inputBindings, outputBindings);
        }
        public static Type Generate(string functionAssemblyName, string typeName, Collection <CustomAttributeBuilder> typeAttributes, Collection <FunctionDescriptor> functions)
        {
            if (functions == null)
            {
                throw new ArgumentNullException("functions");
            }

            AssemblyName    assemblyName    = new AssemblyName(functionAssemblyName);
            AssemblyBuilder assemblyBuilder =
                AppDomain.CurrentDomain.DefineDynamicAssembly(
                    assemblyName,
                    AssemblyBuilderAccess.Run);

            ModuleBuilder mb = assemblyBuilder.DefineDynamicModule(assemblyName.Name);

            TypeBuilder tb = mb.DefineType(typeName, TypeAttributes.Public);

            if (typeAttributes != null)
            {
                foreach (CustomAttributeBuilder attributeBuilder in typeAttributes)
                {
                    tb.SetCustomAttribute(attributeBuilder);
                }
            }

            foreach (FunctionDescriptor function in functions)
            {
                MethodBuilder methodBuilder = tb.DefineMethod(function.Name, MethodAttributes.Public | MethodAttributes.Static);
                Type[]        types         = function.Parameters.Select(p => p.Type).ToArray();
                methodBuilder.SetParameters(types);
                methodBuilder.SetReturnType(typeof(Task));

                if (function.CustomAttributes != null)
                {
                    foreach (CustomAttributeBuilder attributeBuilder in function.CustomAttributes)
                    {
                        methodBuilder.SetCustomAttribute(attributeBuilder);
                    }
                }

                for (int i = 0; i < function.Parameters.Count; i++)
                {
                    ParameterDescriptor parameter        = function.Parameters[i];
                    ParameterBuilder    parameterBuilder = methodBuilder.DefineParameter(i + 1, parameter.Attributes, parameter.Name);
                    if (parameter.CustomAttributes != null)
                    {
                        foreach (CustomAttributeBuilder attributeBuilder in parameter.CustomAttributes)
                        {
                            parameterBuilder.SetCustomAttribute(attributeBuilder);
                        }
                    }
                }

                _invokerMap[function.Name] = function.Invoker;

                MethodInfo invokeMethod = function.Invoker.GetType().GetMethod("Invoke");
                MethodInfo getInvoker   = typeof(FunctionGenerator).GetMethod("GetInvoker", BindingFlags.Static | BindingFlags.Public);

                ILGenerator il = methodBuilder.GetILGenerator();

                LocalBuilder argsLocal    = il.DeclareLocal(typeof(object[]));
                LocalBuilder invokerLocal = il.DeclareLocal(typeof(IFunctionInvoker));

                il.Emit(OpCodes.Nop);

                // declare an array for all parameter values
                il.Emit(OpCodes.Ldc_I4, function.Parameters.Count);
                il.Emit(OpCodes.Newarr, typeof(object));
                il.Emit(OpCodes.Stloc, argsLocal);

                // copy each parameter into the arg array
                for (int i = 0; i < function.Parameters.Count; i++)
                {
                    ParameterDescriptor parameter = function.Parameters[i];

                    il.Emit(OpCodes.Ldloc, argsLocal);
                    il.Emit(OpCodes.Ldc_I4, i);
                    il.Emit(OpCodes.Ldarg, i);

                    // For Out and Ref types, need to do an indirection.
                    if (parameter.Type.IsByRef)
                    {
                        il.Emit(OpCodes.Ldind_Ref);
                    }

                    // Box value types
                    if (parameter.Type.IsValueType)
                    {
                        il.Emit(OpCodes.Box, parameter.Type);
                    }

                    il.Emit(OpCodes.Stelem_Ref);
                }

                // get the invoker instance
                il.Emit(OpCodes.Ldstr, function.Name);
                il.Emit(OpCodes.Call, getInvoker);
                il.Emit(OpCodes.Stloc, invokerLocal);

                // now call the invoker, passing in the args
                il.Emit(OpCodes.Ldloc, invokerLocal);
                il.Emit(OpCodes.Ldloc, argsLocal);
                il.Emit(OpCodes.Callvirt, invokeMethod);

                if (function.Parameters.Any(p => p.Type.IsByRef))
                {
                    LocalBuilder taskLocal        = il.DeclareLocal(typeof(Task));
                    LocalBuilder taskAwaiterLocal = il.DeclareLocal(typeof(TaskAwaiter));

                    // We need to wait on the function's task if we have any out/ref
                    // parameters to ensure they have been populated before we copy them back

                    // Store the result into a local Task
                    // and load it onto the evaluation stack
                    il.Emit(OpCodes.Stloc, taskLocal);
                    il.Emit(OpCodes.Ldloc, taskLocal);

                    // Call "GetAwaiter" on the Task
                    il.Emit(OpCodes.Callvirt, typeof(Task).GetMethod("GetAwaiter", Type.EmptyTypes));

                    // Call "GetResult", which will synchonously wait for the Task to complete
                    il.Emit(OpCodes.Stloc, taskAwaiterLocal);
                    il.Emit(OpCodes.Ldloca, taskAwaiterLocal);
                    il.Emit(OpCodes.Call, typeof(TaskAwaiter).GetMethod("GetResult"));

                    // Copy back out and ref parameters
                    for (int i = 0; i < function.Parameters.Count; i++)
                    {
                        var param = function.Parameters[i];
                        if (!param.Type.IsByRef)
                        {
                            continue;
                        }

                        il.Emit(OpCodes.Ldarg, i);

                        il.Emit(OpCodes.Ldloc, argsLocal);
                        il.Emit(OpCodes.Ldc_I4, i);
                        il.Emit(OpCodes.Ldelem_Ref);
                        il.Emit(OpCodes.Castclass, param.Type.GetElementType());

                        il.Emit(OpCodes.Stind_Ref);
                    }

                    il.Emit(OpCodes.Ldloc, taskLocal);
                }

                il.Emit(OpCodes.Ret);
            }

            Type t = tb.CreateType();

            return(t);
        }
        protected override async Task <Collection <ParameterDescriptor> > GetFunctionParametersAsync(IFunctionInvoker functionInvoker, FunctionMetadata functionMetadata,
                                                                                                     BindingMetadata triggerMetadata, Collection <CustomAttributeBuilder> methodAttributes, Collection <FunctionBinding> inputBindings, Collection <FunctionBinding> outputBindings)
        {
            if (functionInvoker == null)
            {
                throw new ArgumentNullException(nameof(functionInvoker));
            }
            if (functionMetadata == null)
            {
                throw new ArgumentNullException(nameof(functionMetadata));
            }
            if (triggerMetadata == null)
            {
                throw new ArgumentNullException(nameof(triggerMetadata));
            }
            if (methodAttributes == null)
            {
                throw new ArgumentNullException(nameof(methodAttributes));
            }

            var dotNetInvoker = functionInvoker as DotNetFunctionInvoker;

            if (dotNetInvoker == null)
            {
                throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Expected invoker of type '{0}' but received '{1}'", typeof(DotNetFunctionInvoker).Name, functionInvoker.GetType().Name));
            }

            try
            {
                ApplyMethodLevelAttributes(functionMetadata, triggerMetadata, methodAttributes);

                MethodInfo functionTarget = await dotNetInvoker.GetFunctionTargetAsync();

                ParameterInfo[] parameters = functionTarget.GetParameters();
                Collection <ParameterDescriptor> descriptors = new Collection <ParameterDescriptor>();
                IEnumerable <FunctionBinding>    bindings    = inputBindings.Union(outputBindings);
                ParameterDescriptor descriptor = null;
                foreach (var parameter in parameters)
                {
                    // Is it the trigger parameter?
                    if (string.Compare(parameter.Name, triggerMetadata.Name, StringComparison.Ordinal) == 0)
                    {
                        ParameterDescriptor triggerParameter = CreateTriggerParameter(triggerMetadata, parameter.ParameterType);
                        descriptors.Add(triggerParameter);
                    }
                    else
                    {
                        Type parameterType    = parameter.ParameterType;
                        bool parameterIsByRef = parameterType.IsByRef;
                        if (parameterIsByRef)
                        {
                            parameterType = parameterType.GetElementType();
                        }

                        descriptor = new ParameterDescriptor(parameter.Name, parameter.ParameterType);
                        var binding = bindings.FirstOrDefault(b => string.Compare(b.Metadata.Name, parameter.Name, StringComparison.Ordinal) == 0);
                        if (binding != null)
                        {
                            Collection <CustomAttributeBuilder> customAttributes = binding.GetCustomAttributes(parameter.ParameterType);
                            if (customAttributes != null)
                            {
                                foreach (var customAttribute in customAttributes)
                                {
                                    descriptor.CustomAttributes.Add(customAttribute);
                                }
                            }
                        }

                        if (parameter.IsOut)
                        {
                            descriptor.Attributes |= ParameterAttributes.Out;
                        }

                        descriptors.Add(descriptor);
                    }
                }

                // Add any additional required System parameters (if they haven't already been defined by the user)
                if (!descriptors.Any(p => p.Type == typeof(ExecutionContext)))
                {
                    // Add ExecutionContext to provide access to InvocationId, etc.
                    descriptors.Add(new ParameterDescriptor(ScriptConstants.SystemExecutionContextParameterName, typeof(ExecutionContext)));
                }

                if (TryCreateReturnValueParameterDescriptor(functionTarget.ReturnType, bindings, out descriptor))
                {
                    // If a return value binding has been specified, set up an output
                    // binding to map it to. By convention, this is set up as the last
                    // parameter.
                    descriptors.Add(descriptor);
                }

                return(descriptors);
            }
            catch (AggregateException exc)
            {
                if (!(exc.InnerException is CompilationErrorException))
                {
                    throw;
                }
            }
            catch (CompilationErrorException)
            {
            }

            // We were unable to compile the function to get its signature,
            // setup the descriptor with the default parameters
            methodAttributes.Clear();
            return(await base.GetFunctionParametersAsync(functionInvoker, functionMetadata, triggerMetadata, methodAttributes, inputBindings, outputBindings));
        }
        internal static bool TryCreateReturnValueParameterDescriptor(Type functionReturnType, IEnumerable <FunctionBinding> bindings, out ParameterDescriptor descriptor)
        {
            descriptor = null;
            if (string.Equals(functionReturnType.FullName, "Microsoft.FSharp.Core.Unit", StringComparison.Ordinal) ||
                _taskOfUnitType.Value.IsMatch(functionReturnType.FullName))
            {
                return(false);
            }
            if (functionReturnType == typeof(void) || functionReturnType == typeof(Task))
            {
                return(false);
            }

            // Task<T>
            if (typeof(Task).IsAssignableFrom(functionReturnType))
            {
                if (!(functionReturnType.IsGenericType && functionReturnType.GetGenericTypeDefinition() == typeof(Task <>)))
                {
                    throw new InvalidOperationException($"{ScriptConstants.SystemReturnParameterBindingName} cannot be bound to return type {functionReturnType.Name}.");
                }
                functionReturnType = functionReturnType.GetGenericArguments()[0];
            }

            var byRefType = functionReturnType.MakeByRefType();

            descriptor             = new ParameterDescriptor(ScriptConstants.SystemReturnParameterName, byRefType);
            descriptor.Attributes |= ParameterAttributes.Out;

            var returnBinding = bindings.SingleOrDefault(p => p.Metadata.IsReturn);

            if (returnBinding != null)
            {
                Collection <CustomAttributeBuilder> customAttributes = returnBinding.GetCustomAttributes(byRefType);
                if (customAttributes != null)
                {
                    foreach (var customAttribute in customAttributes)
                    {
                        descriptor.CustomAttributes.Add(customAttribute);
                    }
                }
            }

            return(true);
        }
        private bool TryParseTriggerParameter(JObject metadata, out ParameterDescriptor parameterDescriptor, Type parameterType = null)
        {
            parameterDescriptor = null;

            ScriptBindingContext bindingContext = new ScriptBindingContext(metadata);
            ScriptBinding binding = null;
            foreach (var provider in this.Config.BindingProviders)
            {
                if (provider.TryCreate(bindingContext, out binding))
                {
                    break;
                }
            }

            if (binding != null)
            {
                // Construct the Attribute builders for the binding
                var attributes = binding.GetAttributes();
                Collection<CustomAttributeBuilder> attributeBuilders = new Collection<CustomAttributeBuilder>();
                foreach (var attribute in attributes)
                {
                    var attributeBuilder = ExtensionBinding.GetAttributeBuilder(attribute);
                    attributeBuilders.Add(attributeBuilder);
                }

                Type triggerParameterType = parameterType ?? binding.DefaultType;
                parameterDescriptor = new ParameterDescriptor(bindingContext.Name, triggerParameterType, attributeBuilders);

                return true;
            }

            return false;
        }
Exemple #9
0
        public virtual bool TryCreate(FunctionMetadata functionMetadata, out FunctionDescriptor functionDescriptor)
        {
            functionDescriptor = null;

            if (functionMetadata.IsDisabled)
            {
                return(false);
            }

            // parse the bindings
            Collection <FunctionBinding> inputBindings  = FunctionBinding.GetBindings(Config, functionMetadata.InputBindings, FileAccess.Read);
            Collection <FunctionBinding> outputBindings = FunctionBinding.GetBindings(Config, functionMetadata.OutputBindings, FileAccess.Write);

            BindingMetadata triggerMetadata      = functionMetadata.InputBindings.FirstOrDefault(p => p.IsTrigger);
            BindingType     triggerType          = triggerMetadata.Type;
            string          triggerParameterName = triggerMetadata.Name;
            bool            triggerNameSpecified = true;

            if (string.IsNullOrEmpty(triggerParameterName))
            {
                // default the name to simply 'input'
                triggerMetadata.Name = triggerParameterName = "input";
                triggerNameSpecified = false;
            }

            Collection <CustomAttributeBuilder> methodAttributes = new Collection <CustomAttributeBuilder>();
            ParameterDescriptor triggerParameter = null;
            bool omitInputParameter = false;

            switch (triggerType)
            {
            case BindingType.QueueTrigger:
                triggerParameter = ParseQueueTrigger((QueueBindingMetadata)triggerMetadata);
                break;

            case BindingType.BlobTrigger:
                triggerParameter = ParseBlobTrigger((BlobBindingMetadata)triggerMetadata);
                break;

            case BindingType.ServiceBusTrigger:
                triggerParameter = ParseServiceBusTrigger((ServiceBusBindingMetadata)triggerMetadata);
                break;

            case BindingType.TimerTrigger:
                omitInputParameter = true;
                triggerParameter   = ParseTimerTrigger((TimerBindingMetadata)triggerMetadata, typeof(TimerInfo));
                break;

            case BindingType.HttpTrigger:
                if (!triggerNameSpecified)
                {
                    triggerMetadata.Name = triggerParameterName = "req";
                }
                triggerParameter = ParseHttpTrigger((HttpBindingMetadata)triggerMetadata, methodAttributes, typeof(HttpRequestMessage));
                break;

            case BindingType.ManualTrigger:
                triggerParameter = ParseManualTrigger(triggerMetadata, methodAttributes);
                break;
            }

            Collection <ParameterDescriptor> parameters = new Collection <ParameterDescriptor>();

            triggerParameter.IsTrigger = true;
            parameters.Add(triggerParameter);

            // Add a TraceWriter for logging
            parameters.Add(new ParameterDescriptor("log", typeof(TraceWriter)));

            // Add an IBinder to support the binding programming model
            parameters.Add(new ParameterDescriptor("binder", typeof(IBinder)));

            // Add ExecutionContext to provide access to InvocationId, etc.
            parameters.Add(new ParameterDescriptor("context", typeof(ExecutionContext)));

            string           scriptFilePath = Path.Combine(Config.RootScriptPath, functionMetadata.Source);
            IFunctionInvoker invoker        = CreateFunctionInvoker(scriptFilePath, triggerMetadata, functionMetadata, omitInputParameter, inputBindings, outputBindings);

            functionDescriptor = new FunctionDescriptor(functionMetadata.Name, invoker, functionMetadata, parameters, methodAttributes);

            return(true);
        }
        public static Type Generate(string assemblyName, string typeName, Collection <FunctionDescriptor> functions)
        {
            AssemblyName    aName = new AssemblyName(assemblyName);
            AssemblyBuilder ab    =
                AppDomain.CurrentDomain.DefineDynamicAssembly(
                    aName,
                    AssemblyBuilderAccess.RunAndSave);

            ModuleBuilder mb = ab.DefineDynamicModule(aName.Name, aName.Name + ".dll");

            TypeBuilder tb = mb.DefineType(typeName, TypeAttributes.Public);

            foreach (FunctionDescriptor function in functions)
            {
                MethodBuilder methodBuilder = tb.DefineMethod(function.Name, MethodAttributes.Public | MethodAttributes.Static);
                Type[]        types         = function.Parameters.Select(p => p.Type).ToArray();
                methodBuilder.SetParameters(types);
                methodBuilder.SetReturnType(typeof(Task));

                if (function.CustomAttributes != null)
                {
                    foreach (CustomAttributeBuilder attributeBuilder in function.CustomAttributes)
                    {
                        methodBuilder.SetCustomAttribute(attributeBuilder);
                    }
                }

                for (int i = 0; i < function.Parameters.Count; i++)
                {
                    ParameterDescriptor parameter        = function.Parameters[i];
                    ParameterBuilder    parameterBuilder = methodBuilder.DefineParameter(i + 1, parameter.Attributes, parameter.Name);
                    if (parameter.CustomAttributes != null)
                    {
                        foreach (CustomAttributeBuilder attributeBuilder in parameter.CustomAttributes)
                        {
                            parameterBuilder.SetCustomAttribute(attributeBuilder);
                        }
                    }
                }

                _invokerMap[function.Name] = function.Invoker;

                MethodInfo invokeMethod = function.Invoker.GetType().GetMethod("Invoke");
                MethodInfo getInvoker   = typeof(FunctionGenerator).GetMethod("GetInvoker", BindingFlags.Static | BindingFlags.Public);

                ILGenerator il = methodBuilder.GetILGenerator();

                LocalBuilder argsLocal    = il.DeclareLocal(typeof(object[]));
                LocalBuilder invokerLocal = il.DeclareLocal(typeof(IFunctionInvoker));

                il.Emit(OpCodes.Nop);

                // declare an array for all parameter values
                il.Emit(OpCodes.Ldc_I4, function.Parameters.Count);
                il.Emit(OpCodes.Newarr, typeof(object));
                il.Emit(OpCodes.Stloc_0);

                // copy each parameter into the arg array
                for (int i = 0; i < function.Parameters.Count; i++)
                {
                    il.Emit(OpCodes.Ldloc_0);
                    il.Emit(OpCodes.Ldc_I4, i);
                    il.Emit(OpCodes.Ldarg, i);
                    il.Emit(OpCodes.Stelem_Ref);
                }

                // get the invoker instance
                il.Emit(OpCodes.Ldstr, function.Name);
                il.Emit(OpCodes.Call, getInvoker);
                il.Emit(OpCodes.Stloc_1);

                // now call the invoker, passing in the args
                il.Emit(OpCodes.Ldloc_1);
                il.Emit(OpCodes.Ldloc_0);
                il.Emit(OpCodes.Callvirt, invokeMethod);
                il.Emit(OpCodes.Ret);
            }

            Type t = tb.CreateType();

            return(t);
        }
        protected override Collection <ParameterDescriptor> GetFunctionParameters(IFunctionInvoker functionInvoker, FunctionMetadata functionMetadata,
                                                                                  BindingMetadata triggerMetadata, Collection <CustomAttributeBuilder> methodAttributes, Collection <FunctionBinding> inputBindings, Collection <FunctionBinding> outputBindings)
        {
            if (functionInvoker == null)
            {
                throw new ArgumentNullException("functionInvoker");
            }
            if (functionMetadata == null)
            {
                throw new ArgumentNullException("functionMetadata");
            }
            if (triggerMetadata == null)
            {
                throw new ArgumentNullException("triggerMetadata");
            }
            if (methodAttributes == null)
            {
                throw new ArgumentNullException("methodAttributes");
            }

            var csharpInvoker = functionInvoker as CSharpFunctionInvoker;

            if (csharpInvoker == null)
            {
                throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Expected invoker of type '{0}' but received '{1}'", typeof(CSharpFunctionInvoker).Name, functionInvoker.GetType().Name));
            }

            try
            {
                ApplyMethodLevelAttributes(functionMetadata, triggerMetadata, methodAttributes);

                MethodInfo      functionTarget = csharpInvoker.GetFunctionTargetAsync().Result;
                ParameterInfo[] parameters     = functionTarget.GetParameters();
                Collection <ParameterDescriptor> descriptors = new Collection <ParameterDescriptor>();
                IEnumerable <FunctionBinding>    bindings    = inputBindings.Union(outputBindings);
                bool addHttpRequestSystemParameter           = false;
                foreach (var parameter in parameters)
                {
                    // Is it the trigger parameter?
                    if (string.Compare(parameter.Name, triggerMetadata.Name, StringComparison.Ordinal) == 0)
                    {
                        descriptors.Add(CreateTriggerParameterDescriptor(parameter, triggerMetadata));

                        if (triggerMetadata.Type == BindingType.HttpTrigger &&
                            parameter.ParameterType != typeof(HttpRequestMessage))
                        {
                            addHttpRequestSystemParameter = true;
                        }
                    }
                    else
                    {
                        Type parameterType = parameter.ParameterType;
                        if (parameterType.IsByRef)
                        {
                            parameterType = parameterType.GetElementType();
                        }

                        var descriptor = new ParameterDescriptor(parameter.Name, parameter.ParameterType);
                        var binding    = bindings.FirstOrDefault(b => string.Compare(b.Metadata.Name, parameter.Name, StringComparison.Ordinal) == 0);
                        if (binding != null)
                        {
                            Collection <CustomAttributeBuilder> customAttributes = binding.GetCustomAttributes();
                            if (customAttributes != null)
                            {
                                foreach (var customAttribute in customAttributes)
                                {
                                    descriptor.CustomAttributes.Add(customAttribute);
                                }
                            }
                        }

                        if (parameter.IsOut)
                        {
                            descriptor.Attributes |= ParameterAttributes.Out;
                        }

                        descriptors.Add(descriptor);
                    }
                }

                // Add any additional common System parameters
                // Add ExecutionContext to provide access to InvocationId, etc.
                descriptors.Add(new ParameterDescriptor("context", typeof(ExecutionContext)));

                // If we have an HTTP trigger binding but we're not binding
                // to the HttpRequestMessage, require it as a system parameter
                if (addHttpRequestSystemParameter)
                {
                    descriptors.Add(new ParameterDescriptor(ScriptConstants.DefaultSystemTriggerParameterName, typeof(HttpRequestMessage)));
                }

                return(descriptors);
            }
            catch (AggregateException exc)
            {
                if (!(exc.InnerException is CompilationErrorException))
                {
                    throw;
                }
            }
            catch (CompilationErrorException)
            {
            }

            // We were unable to compile the function to get its signature,
            // setup the descriptor with the default parameters
            return(base.GetFunctionParameters(functionInvoker, functionMetadata, triggerMetadata, methodAttributes, inputBindings, outputBindings));
        }
Exemple #12
0
        public static Type Generate(string functionAssemblyName, string typeName, Collection <CustomAttributeBuilder> typeAttributes, Collection <FunctionDescriptor> functions)
        {
            if (functions == null)
            {
                throw new ArgumentNullException("functions");
            }

            AssemblyName    assemblyName    = new AssemblyName(functionAssemblyName);
            AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);

            ModuleBuilder mb = assemblyBuilder.DefineDynamicModule(assemblyName.Name);

            TypeBuilder tb = mb.DefineType(typeName, TypeAttributes.Public);

            if (typeAttributes != null)
            {
                foreach (CustomAttributeBuilder attributeBuilder in typeAttributes)
                {
                    tb.SetCustomAttribute(attributeBuilder);
                }
            }

            foreach (FunctionDescriptor function in functions)
            {
                if (function.Metadata.IsDirect)
                {
                    continue;
                }

                var retValue   = function.Parameters.Where(x => x.Name == ScriptConstants.SystemReturnParameterName).FirstOrDefault();
                var parameters = function.Parameters.Where(x => x != retValue).ToArray();

                MethodBuilder methodBuilder = tb.DefineMethod(function.Name, MethodAttributes.Public | MethodAttributes.Static);
                Type[]        types         = parameters.Select(p => p.Type).ToArray();
                methodBuilder.SetParameters(types);

                Type innerReturnType = null;

                if (retValue == null)
                {
                    methodBuilder.SetReturnType(typeof(Task));
                }
                else
                {
                    // It's critical to set the proper return type for the binder.
                    // The return parameters was added a MakeByRefType, so need to get the inner type.
                    innerReturnType = retValue.Type.GetElementType();

                    // Task<> derives from Task.
                    var actualReturnType = typeof(Task <>).MakeGenericType(innerReturnType);

                    methodBuilder.SetReturnType(actualReturnType);

                    ParameterBuilder parameterBuilder = methodBuilder.DefineParameter(0, ParameterAttributes.Retval, null);

                    if (retValue.CustomAttributes != null)
                    {
                        foreach (CustomAttributeBuilder attributeBuilder in retValue.CustomAttributes)
                        {
                            parameterBuilder.SetCustomAttribute(attributeBuilder);
                        }
                    }
                }

                if (function.CustomAttributes != null)
                {
                    foreach (CustomAttributeBuilder attributeBuilder in function.CustomAttributes)
                    {
                        methodBuilder.SetCustomAttribute(attributeBuilder);
                    }
                }

                for (int i = 0; i < parameters.Length; i++)
                {
                    ParameterDescriptor parameter        = parameters[i];
                    ParameterBuilder    parameterBuilder = methodBuilder.DefineParameter(i + 1, parameter.Attributes, parameter.Name);
                    if (parameter.CustomAttributes != null)
                    {
                        foreach (CustomAttributeBuilder attributeBuilder in parameter.CustomAttributes)
                        {
                            parameterBuilder.SetCustomAttribute(attributeBuilder);
                        }
                    }
                }

                _invokerMap[function.Name] = function.Invoker;

                MethodInfo invokeMethod = function.Invoker.GetType().GetMethod("Invoke");
                MethodInfo getInvoker   = typeof(FunctionGenerator).GetMethod("GetInvoker", BindingFlags.Static | BindingFlags.Public);

                ILGenerator il = methodBuilder.GetILGenerator();

                LocalBuilder argsLocal    = il.DeclareLocal(typeof(object[]));
                LocalBuilder invokerLocal = il.DeclareLocal(typeof(IFunctionInvoker));

                il.Emit(OpCodes.Nop);

                // declare an array for all parameter values
                il.Emit(OpCodes.Ldc_I4, parameters.Length);
                il.Emit(OpCodes.Newarr, typeof(object));
                il.Emit(OpCodes.Stloc, argsLocal);

                // copy each parameter into the arg array
                for (int i = 0; i < parameters.Length; i++)
                {
                    ParameterDescriptor parameter = parameters[i];

                    il.Emit(OpCodes.Ldloc, argsLocal);
                    il.Emit(OpCodes.Ldc_I4, i);
                    il.Emit(OpCodes.Ldarg, i);

                    // For Out and Ref types, need to do an indirection.
                    if (parameter.Type.IsByRef)
                    {
                        il.Emit(OpCodes.Ldind_Ref);
                    }

                    // Box value types
                    if (parameter.Type.IsValueType)
                    {
                        il.Emit(OpCodes.Box, parameter.Type);
                    }

                    il.Emit(OpCodes.Stelem_Ref);
                }

                // get the invoker instance
                il.Emit(OpCodes.Ldstr, function.Name);
                il.Emit(OpCodes.Call, getInvoker);
                il.Emit(OpCodes.Stloc, invokerLocal);

                // now call the invoker, passing in the args
                il.Emit(OpCodes.Ldloc, invokerLocal);
                il.Emit(OpCodes.Ldloc, argsLocal);
                il.Emit(OpCodes.Callvirt, invokeMethod); // pushes a Task<object>

                if (parameters.Any(p => p.Type.IsByRef))
                {
                    LocalBuilder taskLocal        = il.DeclareLocal(typeof(Task <object>));
                    LocalBuilder taskAwaiterLocal = il.DeclareLocal(typeof(TaskAwaiter <object>));

                    // We need to wait on the function's task if we have any out/ref
                    // parameters to ensure they have been populated before we copy them back

                    // Store the result into a local Task
                    // and load it onto the evaluation stack
                    il.Emit(OpCodes.Stloc, taskLocal);
                    il.Emit(OpCodes.Ldloc, taskLocal);

                    // Call "GetAwaiter" on the Task
                    il.Emit(OpCodes.Callvirt, typeof(Task <object>).GetMethod("GetAwaiter", Type.EmptyTypes));

                    // Call "GetResult", which will synchonously wait for the Task to complete
                    il.Emit(OpCodes.Stloc, taskAwaiterLocal);
                    il.Emit(OpCodes.Ldloca, taskAwaiterLocal);
                    il.Emit(OpCodes.Call, typeof(TaskAwaiter <object>).GetMethod("GetResult"));
                    il.Emit(OpCodes.Pop); // ignore GetResult();

                    // Copy back out and ref parameters
                    for (int i = 0; i < parameters.Length; i++)
                    {
                        var param = parameters[i];
                        if (!param.Type.IsByRef)
                        {
                            continue;
                        }

                        il.Emit(OpCodes.Ldarg, i);

                        il.Emit(OpCodes.Ldloc, argsLocal);
                        il.Emit(OpCodes.Ldc_I4, i);
                        il.Emit(OpCodes.Ldelem_Ref);
                        il.Emit(OpCodes.Castclass, param.Type.GetElementType());

                        il.Emit(OpCodes.Stind_Ref);
                    }

                    il.Emit(OpCodes.Ldloc, taskLocal);
                }

                // Need to coerce.
                if (innerReturnType != null)
                {
                    var m  = typeof(FunctionGenerator).GetMethod("Coerce", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
                    var m2 = m.MakeGenericMethod(innerReturnType);
                    il.Emit(OpCodes.Call, m2);
                }

                il.Emit(OpCodes.Ret);
            }

            Type t = tb.CreateTypeInfo().AsType();

            return(t);
        }
        protected override Collection<ParameterDescriptor> GetFunctionParameters(IFunctionInvoker functionInvoker, FunctionMetadata functionMetadata,
            BindingMetadata triggerMetadata, Collection<CustomAttributeBuilder> methodAttributes, Collection<FunctionBinding> inputBindings, Collection<FunctionBinding> outputBindings)
        {
            if (functionInvoker == null)
            {
                throw new ArgumentNullException("functionInvoker");
            }
            if (functionMetadata == null)
            {
                throw new ArgumentNullException("functionMetadata");
            }
            if (triggerMetadata == null)
            {
                throw new ArgumentNullException("triggerMetadata");
            }
            if (methodAttributes == null)
            {
                throw new ArgumentNullException("methodAttributes");
            }

            var dotNetInvoker = functionInvoker as DotNetFunctionInvoker;
            if (dotNetInvoker == null)
            {
                throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Expected invoker of type '{0}' but received '{1}'", typeof(DotNetFunctionInvoker).Name, functionInvoker.GetType().Name));
            }

            try
            {
                ApplyMethodLevelAttributes(functionMetadata, triggerMetadata, methodAttributes);

                MethodInfo functionTarget = dotNetInvoker.GetFunctionTargetAsync().Result;
                ParameterInfo[] parameters = functionTarget.GetParameters();
                Collection<ParameterDescriptor> descriptors = new Collection<ParameterDescriptor>();
                IEnumerable<FunctionBinding> bindings = inputBindings.Union(outputBindings);
                ParameterDescriptor descriptor = null;
                foreach (var parameter in parameters)
                {
                    // Is it the trigger parameter?
                    if (string.Compare(parameter.Name, triggerMetadata.Name, StringComparison.Ordinal) == 0)
                    {
                        ParameterDescriptor triggerParameter = CreateTriggerParameter(triggerMetadata, parameter.ParameterType);
                        descriptors.Add(triggerParameter);
                    }
                    else
                    {
                        Type parameterType = parameter.ParameterType;
                        bool parameterIsByRef = parameterType.IsByRef;
                        if (parameterIsByRef)
                        {
                            parameterType = parameterType.GetElementType();
                        }

                        descriptor = new ParameterDescriptor(parameter.Name, parameter.ParameterType);
                        var binding = bindings.FirstOrDefault(b => string.Compare(b.Metadata.Name, parameter.Name, StringComparison.Ordinal) == 0);
                        if (binding != null)
                        {
                            Collection<CustomAttributeBuilder> customAttributes = binding.GetCustomAttributes(parameter.ParameterType);
                            if (customAttributes != null)
                            {
                                foreach (var customAttribute in customAttributes)
                                {
                                    descriptor.CustomAttributes.Add(customAttribute);
                                }
                            }
                        }

                        // In the C# programming model, IsOut is set for out parameters
                        // In the F# programming model, neither IsOut nor IsIn are set for byref parameters (which are used as out parameters).
                        //   Justification for this cariation of the programming model is that declaring 'out' parameters is (deliberately)
                        //   awkward in F#, they require opening System.Runtime.InteropServices and adding the [<Out>] attribute, and using 
                        //   a byref parameter. In contrast declaring a byref parameter alone (neither labelled In nor Out) is simple enough.
                        if (parameter.IsOut || (functionMetadata.ScriptType == ScriptType.FSharp && parameterIsByRef && !parameter.IsIn))
                        {
                            descriptor.Attributes |= ParameterAttributes.Out;
                        }

                        descriptors.Add(descriptor);
                    }
                }

                // Add any additional required System parameters (if they haven't already been defined by the user)
                if (!descriptors.Any(p => p.Type == typeof(ExecutionContext)))
                {
                    // Add ExecutionContext to provide access to InvocationId, etc.
                    descriptors.Add(new ParameterDescriptor(ScriptConstants.SystemExecutionContextParameterName, typeof(ExecutionContext)));
                }

                // If we have an HTTP trigger binding but no parameter binds to the raw HttpRequestMessage,
                // add it as a system parameter so it is accessible later in the pipeline.
                if (string.Compare(triggerMetadata.Type, "httptrigger", StringComparison.OrdinalIgnoreCase) == 0 &&
                    !descriptors.Any(p => p.Type == typeof(HttpRequestMessage)))
                {
                    descriptors.Add(new ParameterDescriptor(ScriptConstants.SystemTriggerParameterName, typeof(HttpRequestMessage)));
                }

                if (TryCreateReturnValueParameterDescriptor(functionTarget.ReturnType, bindings, out descriptor))
                {
                    // If a return value binding has been specified, set up an output
                    // binding to map it to. By convention, this is set up as the last
                    // parameter.
                    descriptors.Add(descriptor);
                }

                return descriptors;
            }
            catch (AggregateException exc)
            {
                if (!(exc.InnerException is CompilationErrorException))
                {
                    throw;
                }
            }
            catch (CompilationErrorException)
            {
            }

            // We were unable to compile the function to get its signature,
            // setup the descriptor with the default parameters
            methodAttributes.Clear();
            return base.GetFunctionParameters(functionInvoker, functionMetadata, triggerMetadata, methodAttributes, inputBindings, outputBindings);
        }
        internal static bool TryCreateReturnValueParameterDescriptor(Type functionReturnType, IEnumerable<FunctionBinding> bindings, out ParameterDescriptor descriptor)
        {
            descriptor = null;

            var returnBinding = bindings.SingleOrDefault(p => p.Metadata.IsReturn);
            if (returnBinding == null || returnBinding is IResultProcessingBinding)
            {
                return false;
            }

            if (typeof(Task).IsAssignableFrom(functionReturnType))
            {
                if (!(functionReturnType.IsGenericType && functionReturnType.GetGenericTypeDefinition() == typeof(Task<>)))
                {
                    throw new InvalidOperationException($"{ScriptConstants.SystemReturnParameterBindingName} cannot be bound to return type {functionReturnType.Name}.");
                }
                functionReturnType = functionReturnType.GetGenericArguments()[0];
            }

            var byRefType = functionReturnType.MakeByRefType();
            descriptor = new ParameterDescriptor(ScriptConstants.SystemReturnParameterName, byRefType);
            descriptor.Attributes |= ParameterAttributes.Out;

            Collection<CustomAttributeBuilder> customAttributes = returnBinding.GetCustomAttributes(byRefType);
            if (customAttributes != null)
            {
                foreach (var customAttribute in customAttributes)
                {
                    descriptor.CustomAttributes.Add(customAttribute);
                }
            }

            return true;
        }
        protected virtual Collection <ParameterDescriptor> GetFunctionParameters(IFunctionInvoker functionInvoker, FunctionMetadata functionMetadata,
                                                                                 BindingMetadata triggerMetadata, Collection <CustomAttributeBuilder> methodAttributes, Collection <FunctionBinding> inputBindings, Collection <FunctionBinding> outputBindings)
        {
            if (functionInvoker == null)
            {
                throw new ArgumentNullException("functionInvoker");
            }
            if (functionMetadata == null)
            {
                throw new ArgumentNullException("functionMetadata");
            }
            if (triggerMetadata == null)
            {
                throw new ArgumentNullException("triggerMetadata");
            }
            if (methodAttributes == null)
            {
                throw new ArgumentNullException("methodAttributes");
            }

            ParameterDescriptor triggerParameter = null;

            switch (triggerMetadata.Type)
            {
            case BindingType.QueueTrigger:
                triggerParameter = ParseQueueTrigger((QueueBindingMetadata)triggerMetadata);
                break;

            case BindingType.EventHubTrigger:
                triggerParameter = ParseEventHubTrigger((EventHubBindingMetadata)triggerMetadata);
                break;

            case BindingType.BlobTrigger:
                triggerParameter = ParseBlobTrigger((BlobBindingMetadata)triggerMetadata, typeof(Stream));
                break;

            case BindingType.ServiceBusTrigger:
                triggerParameter = ParseServiceBusTrigger((ServiceBusBindingMetadata)triggerMetadata);
                break;

            case BindingType.TimerTrigger:
                triggerParameter = ParseTimerTrigger((TimerBindingMetadata)triggerMetadata, typeof(TimerInfo));
                break;

            case BindingType.HttpTrigger:
                triggerParameter = ParseHttpTrigger((HttpTriggerBindingMetadata)triggerMetadata, typeof(HttpRequestMessage));
                break;

            case BindingType.ManualTrigger:
                triggerParameter = ParseManualTrigger(triggerMetadata);
                break;

            case BindingType.ApiHubTrigger:
                triggerParameter = ParseApiHubTrigger((ApiHubBindingMetadata)triggerMetadata, typeof(Stream));
                break;
            }

            ApplyMethodLevelAttributes(functionMetadata, triggerMetadata, methodAttributes);

            Collection <ParameterDescriptor> parameters = new Collection <ParameterDescriptor>();

            triggerParameter.IsTrigger = true;
            parameters.Add(triggerParameter);

            // Add a TraceWriter for logging
            parameters.Add(new ParameterDescriptor("log", typeof(TraceWriter)));

            // Add an IBinder to support the binding programming model
            parameters.Add(new ParameterDescriptor("binder", typeof(IBinder)));

            // Add ExecutionContext to provide access to InvocationId, etc.
            parameters.Add(new ParameterDescriptor("context", typeof(ExecutionContext)));

            return(parameters);
        }