Exemple #1
0
        /// <summary>
        /// Emits assignment.
        /// </summary>
        /// <remarks>
        /// Pattern: a op= b
        ///
        /// PREPARE a      (prepared)
        /// LOAD a         (prepared,a)
        /// LOAD b         (prepared,a,b)
        /// OP             (prepared,result)
        /// *DUP           (prepared,result,result)
        /// *STORE tmp     (prepared,result)           must be this stack here!
        /// STORE a        ()
        /// *LOAD tmp      (result)
        ///
        /// * only if the resulting value needs to be propagated to the right
        ///
        /// Note: There is a possible improvement: some store operations (SetVariable) may return the value set
        /// which would replace DUP and second temp op.
        /// </remarks>
        internal override PhpTypeCode Emit(CodeGenerator /*!*/ codeGenerator)
        {
            Debug.Assert(access == AccessType.Read || access == AccessType.None || access == AccessType.ReadRef ||
                         access == AccessType.ReadUnknown);
            Statistics.AST.AddNode("Assign.Value");

            ILEmitter il = codeGenerator.IL;

            AccessType old_selector = codeGenerator.AccessSelector;

            codeGenerator.ChainBuilder.Create();

            PhpTypeCode result;

            if (operation == Operations.AssignValue)
            {
                //
                // Access Type = ReadRef/ReadUnknown
                // ---------------------------------
                //
                // f(&$x) { }
                //
                // f($a = $b);
                // f($a = $b =& $c);
                //
                // Destination variable $a is prepared for reference write.
                // A new reference is created and its value set to a deep copy of the result of RHS ($b, $b =& $c).
                // RHS has Read access => it has been dereferenced.
                //

                // PREPARE a:
                codeGenerator.AccessSelector = AccessType.Write;
                lvalue.Emit(codeGenerator);
                codeGenerator.AccessSelector = AccessType.None;

                PhpTypeCode src_type_code = EmitSourceValRead(codeGenerator);

                // RHS should have Read access => should be dereferenced
                Debug.Assert(src_type_code != PhpTypeCode.PhpReference);

                // LOAD BOX b
                codeGenerator.EmitBoxing(src_type_code);

                // makes a copy if necessary:
                if (PhpTypeCodeEnum.IsDeeplyCopied(src_type_code))
                {
                    codeGenerator.EmitVariableCopy(CopyReason.Assigned, rvalue);
                }
            }
            else
            {
                // PREPARE a:
                codeGenerator.AccessSelector = AccessType.Write;
                lvalue.Emit(codeGenerator);
                codeGenerator.AccessSelector = AccessType.None;

                // LOAD b,a (rvalue must be processed first, than +-*/ with lvalue, since lvalu can be changed by rvalue expression)
                //must be the second operand// EmitDestVarRead(codeGenerator);
                PhpTypeCode right_type = EmitSourceValRead(codeGenerator);
                var         rvalue_tmp = codeGenerator.IL.GetTemporaryLocal(PhpTypeCodeEnum.ToType(right_type), false);
                codeGenerator.IL.Emit(OpCodes.Stloc, rvalue_tmp);
                EmitDestVarRead(codeGenerator);
                codeGenerator.IL.Emit(OpCodes.Ldloc, rvalue_tmp);
                codeGenerator.IL.ReturnTemporaryLocal(rvalue_tmp);

                switch (operation)
                {
                    #region Arithmetic

                case Operations.AssignAdd:
                {
                    switch (right_type)
                    {
                    case PhpTypeCode.Integer:
                        result = codeGenerator.EmitMethodCall(Methods.Operators.Add.Object_Int32);
                        break;

                    case PhpTypeCode.Double:
                        result = codeGenerator.EmitMethodCall(Methods.Operators.Add.Object_Double);
                        break;

                    default:
                        codeGenerator.EmitBoxing(right_type);
                        result = codeGenerator.EmitMethodCall(Methods.Operators.Add.Object_Object);
                        break;
                    }
                    break;
                }

                case Operations.AssignSub:
                {
                    switch (right_type)
                    {
                    case PhpTypeCode.Integer:
                        result = codeGenerator.EmitMethodCall(Methods.Operators.Subtract.Object_Int);
                        break;

                    default:
                        codeGenerator.EmitBoxing(right_type);
                        result = codeGenerator.EmitMethodCall(Methods.Operators.Subtract.Object_Object);
                        break;
                    }
                    break;
                }

                case Operations.AssignDiv:
                {
                    switch (right_type)
                    {
                    case PhpTypeCode.Integer:
                        result = codeGenerator.EmitMethodCall(Methods.Operators.Divide.Object_Int32);
                        break;

                    case PhpTypeCode.Double:
                        result = codeGenerator.EmitMethodCall(Methods.Operators.Divide.Object_Double);
                        break;

                    default:
                        codeGenerator.EmitBoxing(right_type);
                        result = codeGenerator.EmitMethodCall(Methods.Operators.Divide.Object_Object);
                        break;
                    }
                    break;
                }

                case Operations.AssignMul:
                {
                    switch (right_type)
                    {
                    case PhpTypeCode.Integer:
                        result = codeGenerator.EmitMethodCall(Methods.Operators.Multiply.Object_Int32);
                        break;

                    case PhpTypeCode.Double:
                        result = codeGenerator.EmitMethodCall(Methods.Operators.Multiply.Object_Double);
                        break;

                    default:
                        codeGenerator.EmitBoxing(right_type);
                        result = codeGenerator.EmitMethodCall(Methods.Operators.Multiply.Object_Object);
                        break;
                    }
                    break;
                }

                case Operations.AssignMod:

                    if (right_type == PhpTypeCode.Integer)
                    {
                        result = codeGenerator.EmitMethodCall(Methods.Operators.Remainder.Object_Int32);
                    }
                    else
                    {
                        codeGenerator.EmitBoxing(right_type);
                        result = codeGenerator.EmitMethodCall(Methods.Operators.Remainder.Object_Object);
                    }
                    break;


                    #endregion

                    #region Bitwise

                case Operations.AssignAnd:
                    codeGenerator.EmitBoxing(right_type);
                    il.Emit(OpCodes.Ldc_I4, (int)Operators.BitOp.And);
                    result = codeGenerator.EmitMethodCall(Methods.Operators.BitOperation);
                    break;

                case Operations.AssignOr:
                    codeGenerator.EmitBoxing(right_type);
                    il.Emit(OpCodes.Ldc_I4, (int)Operators.BitOp.Or);
                    result = codeGenerator.EmitMethodCall(Methods.Operators.BitOperation);
                    break;

                case Operations.AssignXor:
                    codeGenerator.EmitBoxing(right_type);
                    il.Emit(OpCodes.Ldc_I4, (int)Operators.BitOp.Xor);
                    result = codeGenerator.EmitMethodCall(Methods.Operators.BitOperation);
                    break;

                case Operations.AssignShiftLeft:
                    codeGenerator.EmitBoxing(right_type);
                    result = codeGenerator.EmitMethodCall(Methods.Operators.ShiftLeft);
                    break;

                case Operations.AssignShiftRight:
                    codeGenerator.EmitBoxing(right_type);
                    result = codeGenerator.EmitMethodCall(Methods.Operators.ShiftRight);
                    break;

                    #endregion

                    #region String

                case Operations.AssignAppend:
                {
                    if (right_type == PhpTypeCode.String)
                    {
                        result = codeGenerator.EmitMethodCall(Methods.Operators.Append.Object_String);
                    }
                    else if (right_type == PhpTypeCode.PhpBytes)
                    {
                        result = codeGenerator.EmitMethodCall(Methods.PhpBytes.Append_Object_PhpBytes);
                    }
                    else
                    {
                        codeGenerator.EmitBoxing(right_type);
                        result = codeGenerator.EmitMethodCall(Methods.Operators.Append.Object_Object);
                    }
                    break;
                }

                case Operations.AssignPrepend:
                {
                    if (right_type == PhpTypeCode.String)
                    {
                        result = codeGenerator.EmitMethodCall(Methods.Operators.Prepend.Object_String);
                    }
                    else
                    {
                        codeGenerator.EmitBoxing(right_type);
                        result = codeGenerator.EmitMethodCall(Methods.Operators.Prepend.Object_Object);
                    }
                    break;
                }

                    #endregion

                default:
                    Debug.Fail();
                    throw null;
                }

                il.EmitBoxing(result);
            }

            switch (access)
            {
            case AccessType.Read:
            {
                // DUP
                il.Emit(OpCodes.Dup);

                // STORE tmp
                il.Stloc(il.GetAssignmentLocal());

                // STORE prepared, result
                codeGenerator.AccessSelector = AccessType.Write;
                result = lvalue.EmitAssign(codeGenerator);
                codeGenerator.AccessSelector = AccessType.None;
                Debug.Assert(result == PhpTypeCode.Void);

                // LOAD result
                il.Ldloc(il.GetAssignmentLocal());

                result = PhpTypeCode.Object;
                break;
            }

            case AccessType.ReadRef:
            case AccessType.ReadUnknown:

                // STORE prepared,result
                codeGenerator.AccessSelector = AccessType.Write;
                result = lvalue.EmitAssign(codeGenerator);
                codeGenerator.AccessSelector = AccessType.None;
                Debug.Assert(result == PhpTypeCode.Void);

                // loads a reference on the LHS variable:
                codeGenerator.AccessSelector = access;
                codeGenerator.ChainBuilder.Create();
                result = lvalue.Emit(codeGenerator);
                codeGenerator.ChainBuilder.EndRef();
                codeGenerator.AccessSelector = AccessType.None;
                break;

            case AccessType.None:

                // STORE a:
                codeGenerator.AccessSelector = AccessType.Write;
                result = lvalue.EmitAssign(codeGenerator);
                codeGenerator.AccessSelector = AccessType.None;
                Debug.Assert(result == PhpTypeCode.Void);

                break;

            default:
                Debug.Fail("Invalid access type.");
                result = PhpTypeCode.Invalid;
                break;
            }

            codeGenerator.ChainBuilder.End();

            codeGenerator.AccessSelector = old_selector;

            return(result);
        }