private static void EmitAssign(AssigneeKind assigneeKind, Expression node, EmittingContext context, object[] arguments, EmittingContext.LocalHolder value)
        {
            var il = context.Il;

            switch (assigneeKind)
            {
            case AssigneeKind.Parameter:
                il.Ldloc(value);
                var index = Array.IndexOf(context.Parameters, node);
                if (index >= 0)
                {
                    il.Starg(index);
                }
                else
                {
                    GroboIL.Local variable;
                    if (context.VariablesToLocals.TryGetValue((ParameterExpression)node, out variable))
                    {
                        il.Stloc(variable);
                    }
                    else
                    {
                        throw new InvalidOperationException("Unknown parameter " + node);
                    }
                }
                break;

            case AssigneeKind.SimpleArray:
                il.Ldloc(value);
                il.Stind(node.Type);
                break;

            case AssigneeKind.InstanceField:
            case AssigneeKind.StaticField:
                il.Ldloc(value);
                il.Stfld((FieldInfo)((MemberExpression)node).Member);
                break;

            case AssigneeKind.InstanceProperty:
            case AssigneeKind.StaticProperty:
                il.Ldloc(value);
                var memberExpression = (MemberExpression)node;
                il.Call(((PropertyInfo)memberExpression.Member).GetSetMethod(context.SkipVisibility), memberExpression.Expression == null ? null : memberExpression.Expression.Type);
                break;

            case AssigneeKind.IndexedProperty:
            {
                var indexExpression = (IndexExpression)node;
                if (arguments == null)
                {
                    context.EmitLoadArguments(indexExpression.Arguments.ToArray());
                }
                else
                {
                    foreach (var argument in arguments)
                    {
                        if (argument is Expression)
                        {
                            context.EmitLoadArguments((Expression)argument);
                        }
                        else
                        {
                            var local = (EmittingContext.LocalHolder)argument;
                            il.Ldloc(local);
                            local.Dispose();
                        }
                    }
                }
                il.Ldloc(value);
                MethodInfo setter = indexExpression.Indexer.GetSetMethod(context.SkipVisibility);
                if (setter == null)
                {
                    throw new MissingMethodException(indexExpression.Indexer.ReflectedType.ToString(), "set_" + indexExpression.Indexer.Name);
                }
                context.Il.Call(setter, indexExpression.Object.Type);
            }
            break;

            case AssigneeKind.MultiDimensionalArray:
            {
                var  indexExpression = (IndexExpression)node;
                Type arrayType       = indexExpression.Object.Type;
                if (!arrayType.IsArray)
                {
                    throw new InvalidOperationException("An array expected");
                }
                int rank = arrayType.GetArrayRank();
                if (rank != indexExpression.Arguments.Count)
                {
                    throw new InvalidOperationException("Incorrect number of indeces '" + indexExpression.Arguments.Count + "' provided to access an array with rank '" + rank + "'");
                }
                Type indexType = indexExpression.Arguments.First().Type;
                if (indexType != typeof(int))
                {
                    throw new InvalidOperationException("Indexing array with an index of type '" + indexType + "' is not allowed");
                }
                if (arguments == null)
                {
                    context.EmitLoadArguments(indexExpression.Arguments.ToArray());
                }
                else
                {
                    foreach (var argument in arguments)
                    {
                        if (argument is Expression)
                        {
                            context.EmitLoadArguments((Expression)argument);
                        }
                        else
                        {
                            var local = (EmittingContext.LocalHolder)argument;
                            il.Ldloc(local);
                            local.Dispose();
                        }
                    }
                }
                il.Ldloc(value);
                MethodInfo setMethod = arrayType.GetMethod("Set");
                if (setMethod == null)
                {
                    throw new MissingMethodException(arrayType.ToString(), "Set");
                }
                context.Il.Call(setMethod, arrayType);
            }
            break;
            }
        }
        private static object[] EmitAccess(AssigneeKind assigneeKind, Expression node, EmittingContext context)
        {
            object[] arguments = null;
            var      il        = context.Il;

            switch (assigneeKind)
            {
            case AssigneeKind.Parameter:
                var index = Array.IndexOf(context.Parameters, node);
                if (index >= 0)
                {
                    il.Ldarg(index);
                }
                else
                {
                    GroboIL.Local variable;
                    if (context.VariablesToLocals.TryGetValue((ParameterExpression)node, out variable))
                    {
                        il.Ldloc(variable);
                    }
                    else
                    {
                        throw new InvalidOperationException("Unknown parameter " + node);
                    }
                }
                break;

            case AssigneeKind.InstanceField:
            case AssigneeKind.StaticField:
                il.Ldfld((FieldInfo)((MemberExpression)node).Member);
                break;

            case AssigneeKind.InstanceProperty:
            case AssigneeKind.StaticProperty:
                var memberExpression = (MemberExpression)node;
                il.Call(((PropertyInfo)memberExpression.Member).GetGetMethod(context.SkipVisibility), memberExpression.Expression == null ? null : memberExpression.Expression.Type);
                break;

            case AssigneeKind.SimpleArray:
                il.Ldind(node.Type);
                break;

            case AssigneeKind.IndexedProperty:
            {
                var indexExpression = (IndexExpression)node;
                var args            = new List <object>();
                foreach (var argument in indexExpression.Arguments)
                {
                    context.EmitLoadArguments(argument);
                    if (argument.NodeType == ExpressionType.Constant || (argument.NodeType == ExpressionType.MemberAccess && ((MemberExpression)argument).Member.MemberType == MemberTypes.Field && ((FieldInfo)((MemberExpression)argument).Member).IsStatic))
                    {
                        args.Add(argument);
                    }
                    else
                    {
                        var local = context.DeclareLocal(argument.Type);
                        args.Add(local);
                        il.Stloc(local);
                        il.Ldloc(local);
                    }
                }
                arguments = args.ToArray();
                MethodInfo getter = indexExpression.Indexer.GetGetMethod(context.SkipVisibility);
                if (getter == null)
                {
                    throw new MissingMethodException(indexExpression.Indexer.ReflectedType.ToString(), "get_" + indexExpression.Indexer.Name);
                }
                context.Il.Call(getter, indexExpression.Object.Type);
            }
            break;

            case AssigneeKind.MultiDimensionalArray:
            {
                var  indexExpression = (IndexExpression)node;
                Type arrayType       = indexExpression.Object.Type;
                if (!arrayType.IsArray)
                {
                    throw new InvalidOperationException("An array expected");
                }
                int rank = arrayType.GetArrayRank();
                if (rank != indexExpression.Arguments.Count)
                {
                    throw new InvalidOperationException("Incorrect number of indeces '" + indexExpression.Arguments.Count + "' provided to access an array with rank '" + rank + "'");
                }
                Type indexType = indexExpression.Arguments.First().Type;
                if (indexType != typeof(int))
                {
                    throw new InvalidOperationException("Indexing array with an index of type '" + indexType + "' is not allowed");
                }
                var args = new List <object>();
                foreach (var argument in indexExpression.Arguments)
                {
                    context.EmitLoadArguments(argument);
                    if (argument.NodeType == ExpressionType.Constant || (argument.NodeType == ExpressionType.MemberAccess && ((MemberExpression)argument).Member.MemberType == MemberTypes.Field && ((FieldInfo)((MemberExpression)argument).Member).IsStatic))
                    {
                        args.Add(argument);
                    }
                    else
                    {
                        var local = context.DeclareLocal(argument.Type);
                        args.Add(local);
                        il.Stloc(local);
                        il.Ldloc(local);
                    }
                }
                arguments = args.ToArray();
                MethodInfo getMethod = arrayType.GetMethod("Get");
                if (getMethod == null)
                {
                    throw new MissingMethodException(arrayType.ToString(), "Get");
                }
                context.Il.Call(getMethod, arrayType);
            }
            break;
            }
            return(arguments);
        }