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); }
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) { 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; }
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)); }
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); }