예제 #1
0
        protected override bool EmitInternal(BinaryExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType)
        {
            var          il     = context.Il;
            var          left   = node.Left;
            var          right  = node.Right;
            bool         result = false;
            Type         assigneeType;
            AssigneeKind assigneeKind;
            bool         checkNullReferences = context.Options.HasFlag(CompilerOptions.CheckNullReferences);

            extend |= context.Options.HasFlag(CompilerOptions.ExtendOnAssign);

            GroboIL.Label assigneeIsNullLabel     = null;
            bool          assigneeIsNullLabelUsed = false;

            switch (left.NodeType)
            {
            case ExpressionType.Parameter:
                assigneeType        = null;
                assigneeKind        = AssigneeKind.Parameter;
                checkNullReferences = false;
                break;

            case ExpressionType.MemberAccess:
                var memberExpression = (MemberExpression)left;
                if (memberExpression.Expression == null)
                {
                    assigneeType        = null;
                    assigneeKind        = memberExpression.Member is FieldInfo ? AssigneeKind.StaticField : AssigneeKind.StaticProperty;
                    checkNullReferences = false;
                }
                else
                {
                    bool closureAssign = memberExpression.Expression == context.ParsedLambda.ClosureParameter || memberExpression.Expression.Type.IsStaticClosure();
                    checkNullReferences &= !closureAssign;
                    if (node.NodeType != ExpressionType.Assign && context.CanReturn)
                    {
                        result |= ExpressionEmittersCollection.Emit(memberExpression.Expression, context, returnDefaultValueLabel, ResultType.ByRefValueTypesOnly, extend, out assigneeType);
                    }
                    else
                    {
                        assigneeIsNullLabel     = !closureAssign && context.CanReturn ? il.DefineLabel("assigneeIsNull") : null;
                        assigneeIsNullLabelUsed = ExpressionEmittersCollection.Emit(memberExpression.Expression, context, assigneeIsNullLabel, ResultType.ByRefValueTypesOnly, extend, out assigneeType);
                    }
                    assigneeKind = memberExpression.Member is FieldInfo ? AssigneeKind.InstanceField : AssigneeKind.InstanceProperty;
                }
                break;

            case ExpressionType.Index:
                var indexExpression = (IndexExpression)left;
                if (indexExpression.Object == null)
                {
                    throw new InvalidOperationException("Indexing of null object is invalid");
                }
                if ((indexExpression.Object.Type.IsArray && indexExpression.Object.Type.GetArrayRank() == 1) || indexExpression.Object.Type.IsList())
                {
                    if (node.NodeType != ExpressionType.Assign && context.CanReturn)
                    {
                        result |= ArrayIndexExpressionEmitter.Emit(indexExpression.Object, indexExpression.Arguments.Single(), context, returnDefaultValueLabel, ResultType.ByRefAll, extend, out assigneeType);
                        checkNullReferences = false;
                    }
                    else
                    {
                        assigneeIsNullLabel     = context.CanReturn ? il.DefineLabel("assigneeIsNull") : null;
                        assigneeIsNullLabelUsed = ArrayIndexExpressionEmitter.Emit(indexExpression.Object, indexExpression.Arguments.Single(), context, assigneeIsNullLabel, ResultType.ByRefAll, extend, out assigneeType);
                    }
                    assigneeKind = AssigneeKind.SimpleArray;
                }
                else
                {
                    if (node.NodeType != ExpressionType.Assign && context.CanReturn)
                    {
                        result |= ExpressionEmittersCollection.Emit(indexExpression.Object, context, returnDefaultValueLabel, ResultType.ByRefValueTypesOnly, extend, out assigneeType);
                    }
                    else
                    {
                        assigneeIsNullLabel     = context.CanReturn ? il.DefineLabel("assigneeIsNull") : null;
                        assigneeIsNullLabelUsed = ExpressionEmittersCollection.Emit(indexExpression.Object, context, assigneeIsNullLabel, ResultType.ByRefValueTypesOnly, extend, out assigneeType);
                    }
                    assigneeKind = indexExpression.Indexer != null ? AssigneeKind.IndexedProperty : AssigneeKind.MultiDimensionalArray;
                }
                break;

            default:
                throw new InvalidOperationException("Unable to assign to an expression of type '" + left.NodeType + "'");
            }
            if (assigneeType != null && assigneeType.IsValueType)
            {
                using (var temp = context.DeclareLocal(assigneeType))
                {
                    il.Stloc(temp);
                    il.Ldloca(temp);
                }
                assigneeType = assigneeType.MakeByRefType();
            }

            if (node.NodeType == ExpressionType.Assign)
            {
                if (!checkNullReferences)
                {
                    if (whatReturn == ResultType.Void)
                    {
                        EmitAssign(assigneeKind, left, context, null, right);
                    }
                    else
                    {
                        if (assigneeKind == AssigneeKind.Parameter)
                        {
                            EmitAssign(assigneeKind, left, context, null, right);
                            EmitAccess(assigneeKind, left, context);
                        }
                        else
                        {
                            context.EmitLoadArguments(right);
                            using (var value = context.DeclareLocal(right.Type))
                            {
                                il.Stloc(value);
                                EmitAssign(assigneeKind, left, context, null, value);
                                il.Ldloc(value);
                            }
                        }
                    }
                }
                else
                {
                    if (whatReturn == ResultType.Void)
                    {
                        il.Dup();
                        var skipAssigneeLabel = il.DefineLabel("skipAssignee");
                        il.Brfalse(skipAssigneeLabel);
                        EmitAssign(assigneeKind, left, context, null, right);
                        var returnLabel = il.DefineLabel("return");
                        il.Br(returnLabel);
                        context.MarkLabelAndSurroundWithSP(skipAssigneeLabel);
                        il.Pop();
                        context.MarkLabelAndSurroundWithSP(returnLabel);
                    }
                    else
                    {
                        // load value
                        var  rightIsNullLabel = context.CanReturn ? il.DefineLabel("rightIsNull") : null;
                        Type valueType;
                        bool labelUsed = ExpressionEmittersCollection.Emit(right, context, rightIsNullLabel, out valueType); // stack: [address, value]
                        if (right.Type == typeof(bool) && valueType == typeof(bool?))
                        {
                            context.ConvertFromNullableBoolToBool();
                        }
                        if (labelUsed && context.CanReturn)
                        {
                            context.EmitReturnDefaultValue(right.Type, rightIsNullLabel, il.DefineLabel("rightIsNotNull"));
                        }
                        using (var value = context.DeclareLocal(right.Type))
                        {
                            il.Stloc(value);
                            il.Dup();
                            var skipAssigneeLabel = il.DefineLabel("skipAssignee");
                            il.Brfalse(skipAssigneeLabel);
                            EmitAssign(assigneeKind, left, context, null, value);
                            var returnValueLabel = il.DefineLabel("returnValue");
                            il.Br(returnValueLabel);
                            context.MarkLabelAndSurroundWithSP(skipAssigneeLabel);
                            il.Pop();
                            context.MarkLabelAndSurroundWithSP(returnValueLabel);
                            il.Ldloc(value);
                        }
                    }
                }
            }
            else
            {
                if (checkNullReferences)
                {
                    il.Dup();
                    il.Brfalse(returnDefaultValueLabel);
                    result = true;
                }
                if (assigneeType != null)
                {
                    il.Dup();
                }
                object[] arguments = EmitAccess(assigneeKind, left, context);
                context.EmitLoadArguments(right);
                context.EmitArithmeticOperation(GetOp(node.NodeType), left.Type, left.Type, right.Type, node.Method);
                if (whatReturn == ResultType.Void)
                {
                    EmitAssign(assigneeKind, left, context, arguments);
                }
                else
                {
                    if (assigneeKind == AssigneeKind.Parameter)
                    {
                        EmitAssign(assigneeKind, left, context, arguments);
                        EmitAccess(assigneeKind, left, context);
                    }
                    else
                    {
                        using (var temp = context.DeclareLocal(left.Type))
                        {
                            il.Stloc(temp);
                            EmitAssign(assigneeKind, left, context, arguments, temp);
                            il.Ldloc(temp);
                        }
                    }
                }
            }
            resultType = whatReturn == ResultType.Void ? typeof(void) : left.Type;
            if (assigneeIsNullLabelUsed)
            {
                context.EmitReturnDefaultValue(resultType, assigneeIsNullLabel, il.DefineLabel("assigneeIsNotNull"));
            }
            return(result);
        }
        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);
        }