protected override bool EmitInternal(ParameterExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType)
        {
            if (whatReturn == ResultType.Void)
            {
                resultType = typeof(void);
                return(false);
            }
            ConstructorInfo constructor = node.Type.GetConstructor(Type.EmptyTypes);

            extend &= node != context.ParsedLambda.ClosureParameter && node != context.ParsedLambda.ConstantsParameter && !node.Type.IsStaticClosure() &&
                      ((node.Type.IsClass && constructor != null) || node.Type.IsArray);
            int index = Array.IndexOf(context.Parameters, node);

            if (index >= 0)
            {
                if (extend)
                {
                    context.Il.Ldarg(index);
                    var parameterIsNotNullLabel = context.Il.DefineLabel("parameterIsNotNull");
                    context.Il.Brtrue(parameterIsNotNullLabel);
                    context.Il.Ldarga(index);
                    context.Create(node.Type);
                    context.Il.Stind(node.Type);
                    context.MarkLabelAndSurroundWithSP(parameterIsNotNullLabel);
                }

                switch (whatReturn)
                {
                case ResultType.Value:
                    context.Il.Ldarg(index); // stack: [parameter]
                    resultType = node.Type;
                    break;

                case ResultType.ByRefAll:
                    context.Il.Ldarga(index); // stack: [&parameter]
                    resultType = node.Type.MakeByRefType();
                    break;

                case ResultType.ByRefValueTypesOnly:
                    if (node.Type.IsValueType)
                    {
                        context.Il.Ldarga(index); // stack: [&parameter]
                        resultType = node.Type.MakeByRefType();
                    }
                    else
                    {
                        context.Il.Ldarg(index); // stack: [parameter]
                        resultType = node.Type;
                    }
                    break;

                default:
                    throw new NotSupportedException("Result type '" + whatReturn + "' is not supported");
                }
                return(false);
            }
            GroboIL.Local variable;
            if (context.VariablesToLocals.TryGetValue(node, out variable))
            {
                if (extend)
                {
                    context.Il.Ldloc(variable);
                    var parameterIsNotNullLabel = context.Il.DefineLabel("parameterIsNotNull");
                    context.Il.Brtrue(parameterIsNotNullLabel);
                    context.Create(node.Type);
                    context.Il.Stloc(variable);
                    context.MarkLabelAndSurroundWithSP(parameterIsNotNullLabel);
                }
                switch (whatReturn)
                {
                case ResultType.Value:
                    context.Il.Ldloc(variable); // stack: [variable]
                    resultType = node.Type;
                    break;

                case ResultType.ByRefAll:
                    context.Il.Ldloca(variable); // stack: [&variable]
                    resultType = node.Type.MakeByRefType();
                    break;

                case ResultType.ByRefValueTypesOnly:
                    if (node.Type.IsValueType)
                    {
                        context.Il.Ldloca(variable); // stack: [&variable]
                        resultType = node.Type.MakeByRefType();
                    }
                    else
                    {
                        context.Il.Ldloc(variable); // stack: [variable]
                        resultType = node.Type;
                    }
                    break;

                default:
                    throw new NotSupportedException("Result type '" + whatReturn + "' is not supported");
                }
                return(false);
            }
            throw new InvalidOperationException("Unknown parameter " + node);
        }
        protected override bool EmitInternal(IndexExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType)
        {
            if (node.Object == null)
            {
                throw new InvalidOperationException("Indexing of null object is invalid");
            }
            if (node.Object.Type.IsArray && node.Object.Type.GetArrayRank() == 1)
            {
                return(ExpressionEmittersCollection.Emit(Expression.ArrayIndex(node.Object, node.Arguments.Single()), context, returnDefaultValueLabel, whatReturn, extend, out resultType));
            }
            if (node.Object.Type.IsList())
            {
                return(ArrayIndexExpressionEmitter.Emit(node.Object, node.Arguments.Single(), context, returnDefaultValueLabel, whatReturn, extend, out resultType));
            }
            Type objectType;
            bool result = ExpressionEmittersCollection.Emit(node.Object, context, returnDefaultValueLabel, ResultType.ByRefValueTypesOnly, extend, out objectType);

            if (objectType.IsValueType)
            {
                using (var temp = context.DeclareLocal(objectType))
                {
                    context.Il.Stloc(temp);
                    context.Il.Ldloca(temp);
                }
            }
            if (context.Options.HasFlag(CompilerOptions.CheckNullReferences))
            {
                result |= context.EmitNullChecking(objectType, returnDefaultValueLabel);
            }
            if (node.Indexer != null)
            {
                context.EmitLoadArguments(node.Arguments.ToArray());
                MethodInfo getter = node.Indexer.GetGetMethod(context.SkipVisibility);
                if (getter == null)
                {
                    throw new MissingMethodException(node.Indexer.ReflectedType.ToString(), "get_" + node.Indexer.Name);
                }
                GroboIL.Label doneLabel = null;
                if (node.Object.Type.IsDictionary())
                {
                    var valueType   = node.Object.Type.GetGenericArguments()[1];
                    var constructor = valueType.GetConstructor(Type.EmptyTypes);
                    extend   &= (valueType.IsClass && constructor != null) || valueType.IsArray || valueType.IsValueType || valueType == typeof(string);
                    doneLabel = context.Il.DefineLabel("done");
                    using (var dict = context.DeclareLocal(node.Object.Type))
                        using (var key = context.DeclareLocal(node.Arguments.Single().Type))
                            using (var value = context.DeclareLocal(valueType))
                            {
                                context.Il.Stloc(key);    // key = arg0; stack: [dict]
                                context.Il.Dup();         // stack: [dict, dict]
                                context.Il.Stloc(dict);   // stack: [dict]
                                context.Il.Ldloc(key);    // stack: [dict, key]
                                context.Il.Ldloca(value); // stack: [dict, key, ref value]
                                var tryGetValueMethod = node.Object.Type.GetMethod("TryGetValue", BindingFlags.Public | BindingFlags.Instance);
                                if (tryGetValueMethod == null)
                                {
                                    throw new MissingMethodException(node.Object.Type.Name, "TryGetValue");
                                }
                                context.Il.Call(tryGetValueMethod, node.Object.Type); // stack: [dict.TryGetValue(key, out value)]
                                var loadResultLabel = context.Il.DefineLabel("loadResult");
                                context.Il.Brtrue(loadResultLabel);                   // if(dict.TryGetValue(key, out result)) goto loadResult; stack: []
                                if (valueType.IsValueType)
                                {
                                    context.Il.Ldloca(value);
                                    context.Il.Initobj(valueType); // value = default(valueType)
                                    context.Il.Ldloc(value);       // stack: [default(valueType)]
                                }
                                else if (valueType == typeof(string))
                                {
                                    context.Il.Ldstr("");
                                }
                                else
                                {
                                    context.Create(valueType);
                                }
                                context.Il.Stloc(value);
                                if (extend)
                                {
                                    context.Il.Ldloc(dict);  // stack: [dict]
                                    context.Il.Ldloc(key);   // stack: [dict, key]
                                    context.Il.Ldloc(value); // stack: [dict, key, value]
                                    var setter = node.Indexer.GetSetMethod(context.SkipVisibility);
                                    if (setter == null)
                                    {
                                        throw new MissingMethodException(node.Indexer.ReflectedType.ToString(), "set_" + node.Indexer.Name);
                                    }
                                    context.Il.Call(setter, node.Object.Type); // dict.set_Item(key, default(valueType)); stack: []
                                }
                                context.MarkLabelAndSurroundWithSP(loadResultLabel);
                                context.Il.Ldloc(value);
                            }
                }
                if (doneLabel != null)
                {
                    context.MarkLabelAndSurroundWithSP(doneLabel);
                }
                else
                {
                    context.Il.Call(getter, node.Object.Type);
                }
            }
            else
            {
                Type arrayType = node.Object.Type;
                if (!arrayType.IsArray)
                {
                    throw new InvalidOperationException("An array expected");
                }
                int rank = arrayType.GetArrayRank();
                if (rank != node.Arguments.Count)
                {
                    throw new InvalidOperationException("Incorrect number of indeces '" + node.Arguments.Count + "' provided to access an array with rank '" + rank + "'");
                }
                Type indexType = node.Arguments.First().Type;
                if (indexType != typeof(int))
                {
                    throw new InvalidOperationException("Indexing array with an index of type '" + indexType + "' is not allowed");
                }
                context.EmitLoadArguments(node.Arguments.ToArray());
                MethodInfo getMethod = arrayType.GetMethod("Get");
                if (getMethod == null)
                {
                    throw new MissingMethodException(arrayType.ToString(), "Get");
                }
                context.Il.Call(getMethod, arrayType);
            }
            resultType = node.Type;
            return(result);
        }
        public static bool Emit(Expression zarr, Expression zindex, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType)
        {
            var arrayType = zarr.Type;
            var isArray   = arrayType.IsArray;

            if (!isArray && !arrayType.IsList())
            {
                throw new InvalidOperationException("Unable to perform array index operator to type '" + arrayType + "'");
            }
            var     itemType = isArray ? arrayType.GetElementType() : arrayType.GetGenericArguments()[0];
            GroboIL il       = context.Il;

            EmittingContext.LocalHolder arrayIndex = null;
            bool extendArray        = extend && (CanAssign(zarr) || !isArray);
            bool extendArrayElement = extend && itemType.IsClass;
            var  result             = false;

            if (!extendArray)
            {
                result |= ExpressionEmittersCollection.Emit(zarr, context, returnDefaultValueLabel, ResultType.Value, extend, out arrayType); // stack: [array]
                if (context.Options.HasFlag(CompilerOptions.CheckNullReferences))
                {
                    result = true;
                    il.Dup();                              // stack: [array, array]
                    il.Brfalse(returnDefaultValueLabel);   // if(array == null) goto returnDefaultValue; stack: [array]
                }
                EmitLoadIndex(zindex, context, arrayType); // stack: [array, arrayIndex]
                if (context.Options.HasFlag(CompilerOptions.CheckArrayIndexes))
                {
                    result     = true;
                    arrayIndex = context.DeclareLocal(typeof(int));
                    il.Stloc(arrayIndex); // arrayIndex = index; stack: [array]
                    il.Dup();             // stack: [array, array]
                    if (isArray)
                    {
                        il.Ldlen(); // stack: [array, array.Length]
                    }
                    else
                    {
                        EmitLoadField(context, arrayType, arrayType.GetField("_size", BindingFlags.Instance | BindingFlags.NonPublic));
                    }
                    il.Ldloc(arrayIndex);                   // stack: [array, array.Length, arrayIndex]
                    il.Ble(returnDefaultValueLabel, false); // if(array.Length <= arrayIndex) goto returnDefaultValue; stack: [array]
                    il.Ldloc(arrayIndex);                   // stack: [array, arrayIndex]
                    il.Ldc_I4(0);                           // stack: [array, arrayIndex, 0]
                    il.Blt(returnDefaultValueLabel, false); // if(arrayIndex < 0) goto returnDefaultValue; stack: [array]
                }
                else if (extendArrayElement || !isArray)
                {
                    arrayIndex = context.DeclareLocal(typeof(int));
                    il.Stloc(arrayIndex); // arrayIndex = index; stack: [array]
                }
            }
            else
            {
                EmittingContext.LocalHolder arrayOwner = null;
                switch (zarr.NodeType)
                {
                case ExpressionType.Parameter:
                case ExpressionType.ArrayIndex:
                case ExpressionType.Index:
                    Type type;
                    ExpressionEmittersCollection.Emit(zarr, context, returnDefaultValueLabel, ResultType.ByRefAll, true, out type); // stack: [ref array]
                    arrayOwner = context.DeclareLocal(type);
                    il.Dup();                                                                                                       // stack: [ref array, ref array]
                    il.Stloc(arrayOwner);                                                                                           // arrayOwner = ref array; stack: [ref array]
                    il.Ldind(zarr.Type);                                                                                            // stack: [array]
                    break;

                case ExpressionType.MemberAccess:
                    var  memberExpression = (MemberExpression)zarr;
                    Type memberType;
                    context.EmitMemberAccess(memberExpression, returnDefaultValueLabel, context.Options.HasFlag(CompilerOptions.CheckNullReferences), true, ResultType.ByRefValueTypesOnly, out memberType, out arrayOwner); // stack: [array]
                    break;

                default:
                    throw new InvalidOperationException("Cannot extend array for expression with node type '" + zarr.NodeType + "'");
                }
                if (context.Options.HasFlag(CompilerOptions.CheckNullReferences))
                {
                    il.Dup();                            // stack: [array, array]
                    il.Brfalse(returnDefaultValueLabel); // if(array == null) goto returnDefaultValue; stack: [array]
                }
                EmitLoadIndex(zindex, context, arrayType);
                result     = true;
                arrayIndex = context.DeclareLocal(typeof(int));
                il.Stloc(arrayIndex);                   // arrayIndex = index; stack: [array]
                il.Ldloc(arrayIndex);                   // stack: [array, arrayIndex]
                il.Ldc_I4(0);                           // stack: [array, arrayIndex, 0]
                il.Blt(returnDefaultValueLabel, false); // if(arrayIndex < 0) goto returnDefaultValue; stack: [array]
                il.Dup();                               // stack: [array, array]
                if (isArray)
                {
                    il.Ldlen(); // stack: [array, array.Length]
                }
                else
                {
                    EmitLoadField(context, arrayType, arrayType.GetField("_size", BindingFlags.Instance | BindingFlags.NonPublic));
                }
                il.Ldloc(arrayIndex);          // stack: [array, array.Length, arrayIndex]
                var bigEnoughLabel = il.DefineLabel("bigEnough");
                il.Bgt(bigEnoughLabel, false); // if(array.Length > arrayIndex) goto bigEnough; stack: [array]
                using (var array = context.DeclareLocal(arrayType))
                {
                    il.Stloc(array); // stack: []
                    if (!isArray)
                    {
                        EnsureCount(context, array, arrayIndex, arrayType);
                    }
                    else
                    {
                        il.Ldloca(array);                                                         // stack: [ref array]
                        il.Ldloc(arrayIndex);                                                     // stack: [ref array, arrayIndex]
                        il.Ldc_I4(1);                                                             // stack: [ref array, arrayIndex, 1]
                        il.Add();                                                                 // stack: [ref array, arrayIndex + 1]
                        il.Call(arrayResizeMethod.MakeGenericMethod(arrayType.GetElementType())); // Array.Resize(ref array, 1 + arrayIndex); stack: []

                        switch (zarr.NodeType)
                        {
                        case ExpressionType.Parameter:
                        case ExpressionType.ArrayIndex:
                        case ExpressionType.Index:
                            il.Ldloc(arrayOwner); // stack: [ref parameter]
                            il.Ldloc(array);      // stack: [ref parameter, array]
                            il.Stind(arrayType);  // parameter = array; stack: []
                            break;

                        case ExpressionType.MemberAccess:
                            var memberExpression = (MemberExpression)zarr;
                            if (memberExpression.Expression != null)
                            {
                                il.Ldloc(arrayOwner);
                            }
                            il.Ldloc(array);
                            switch (memberExpression.Member.MemberType)
                            {
                            case MemberTypes.Field:
                                il.Stfld((FieldInfo)memberExpression.Member);
                                break;

                            case MemberTypes.Property:
                                var propertyInfo = (PropertyInfo)memberExpression.Member;
                                var setter       = propertyInfo.GetSetMethod(context.SkipVisibility);
                                if (setter == null)
                                {
                                    throw new MissingMethodException(propertyInfo.ReflectedType.ToString(), "set_" + propertyInfo.Name);
                                }
                                il.Call(setter, memberExpression.Expression == null ? null : memberExpression.Expression.Type);
                                break;

                            default:
                                throw new NotSupportedException("Member type '" + memberExpression.Member.MemberType + "' is not supported");
                            }
                            break;

                        default:
                            throw new InvalidOperationException("Unable to assign array to an expression with node type '" + zarr.NodeType);
                        }
                    }
                    il.Ldloc(array);
                    context.MarkLabelAndSurroundWithSP(bigEnoughLabel);
                }
            }

            if (!isArray)
            {
                // TODO: это злобно, лист при всех операциях меняет _version, а мы нет
                EmitLoadField(context, arrayType, arrayType.GetField("_items", BindingFlags.Instance | BindingFlags.NonPublic));
                arrayType = itemType.MakeArrayType();
            }

            if (extendArrayElement)
            {
                // stack: [array]
                var constructor = itemType.GetConstructor(Type.EmptyTypes);
                if (itemType.IsArray || constructor != null)
                {
                    using (var array = context.DeclareLocal(arrayType))
                    {
                        il.Dup();             // stack: [array, array]
                        il.Stloc(array);      // stack: [array]
                        il.Ldloc(arrayIndex); // stack: [array, arrayIndex]
                        il.Ldelem(itemType);  // stack: [array[arrayIndex]]
                        var elementIsNotNullLabel = il.DefineLabel("elementIsNotNull");
                        il.Brtrue(elementIsNotNullLabel);
                        il.Ldloc(array);
                        il.Ldloc(arrayIndex);
                        context.Create(itemType);
                        il.Stelem(itemType);
                        context.MarkLabelAndSurroundWithSP(elementIsNotNullLabel);
                        il.Ldloc(array);
                    }
                }
            }
            if (arrayIndex != null)
            {
                il.Ldloc(arrayIndex);
                arrayIndex.Dispose();
            }
            switch (whatReturn)
            {
            case ResultType.ByRefAll:
                il.Ldelema(itemType);
                resultType = itemType.MakeByRefType();
                break;

            case ResultType.ByRefValueTypesOnly:
                if (itemType.IsValueType)
                {
                    il.Ldelema(itemType);
                    resultType = itemType.MakeByRefType();
                }
                else
                {
                    il.Ldelem(itemType); // stack: [array[arrayIndex]]
                    resultType = itemType;
                }
                break;

            default:
                il.Ldelem(itemType); // stack: [array[arrayIndex]]
                resultType = itemType;
                break;
            }
            return(result);
        }