public static Type Generate()

in src/WebJobs.Script/Description/FunctionGenerator.cs [27:221]


        public static Type Generate(string functionAssemblyName, string typeName, Collection<CustomAttributeBuilder> typeAttributes, ICollection<FunctionDescriptor> functions)
        {
            if (functions == null)
            {
                throw new ArgumentNullException(nameof(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.FirstOrDefault(x => x.Name == ScriptConstants.SystemReturnParameterName);
                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;
        }