Example #1
0
		/// <remarks>
		/// Nothing is expected at the evaluation stack. If AST node is read by other node,
		/// the operation result is left at the stack, otherwise it is poped from the stack.
		/// </remarks>
		/// <include file='Doc/Nodes.xml' path='doc/method[@name="Emit"]/*'/>
		internal override PhpTypeCode Emit(CodeGenerator codeGenerator)
		{
			Debug.Assert(access == AccessType.None || access == AccessType.Read);
			Statistics.AST.AddNode("BinaryEx");

			PhpTypeCode returned_typecode;
			PhpTypeCode lo_typecode;
			PhpTypeCode ro_typecode;

			switch (operation)
			{
				#region Arithmetic Operations

				case Operations.Add:
					// Template: x + y : Operators.Add(x,y) [overloads]

                    switch (lo_typecode = leftExpr.Emit(codeGenerator))
                    {
                        case PhpTypeCode.Double:
                            switch (ro_typecode = rightExpr.Emit(codeGenerator))
                            {
                                case PhpTypeCode.Integer:
                                    codeGenerator.IL.Emit(OpCodes.Conv_R8);
                                    goto case PhpTypeCode.Double;   // fallback:
                                case PhpTypeCode.Double:
                                    codeGenerator.IL.Emit(OpCodes.Add);
                                    returned_typecode = PhpTypeCode.Double;
                                    break;
                                default:
                                    codeGenerator.EmitBoxing(ro_typecode);
                                    returned_typecode = codeGenerator.EmitMethodCall(Methods.Operators.Add.Double_Object);
                                    break;
                            }
                            
                            break;
                        default:
                            codeGenerator.EmitBoxing(lo_typecode);
                            ro_typecode = rightExpr.Emit(codeGenerator);

                            switch (ro_typecode)
                            {
                                case PhpTypeCode.Integer:
                                    returned_typecode = codeGenerator.EmitMethodCall(Methods.Operators.Add.Object_Int32);
                                    break;

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

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

				case Operations.Sub:
					//Template: "x - y"        Operators.Subtract(x,y) [overloads]
					lo_typecode = leftExpr.Emit(codeGenerator);
                    switch (lo_typecode)
                    {
                        case PhpTypeCode.Integer:
                            codeGenerator.EmitBoxing(rightExpr.Emit(codeGenerator));
                            returned_typecode = codeGenerator.EmitMethodCall(Methods.Operators.Subtract.Int32_Object);
                            break;
                        case PhpTypeCode.Double:
                            switch (ro_typecode = rightExpr.Emit(codeGenerator))
                            {
                                case PhpTypeCode.Integer:
                                    codeGenerator.IL.Emit(OpCodes.Conv_R8);
                                    goto case PhpTypeCode.Double;   // fallback:
                                case PhpTypeCode.Double:
                                    codeGenerator.IL.Emit(OpCodes.Sub);
                                    returned_typecode = PhpTypeCode.Double;
                                    break;
                                default:
                                    codeGenerator.EmitBoxing(ro_typecode);
                                    returned_typecode = codeGenerator.EmitMethodCall(Methods.Operators.Subtract.Double_Object);
                                    break;
                            }
                            
                            break;
                        default:
                            codeGenerator.EmitBoxing(lo_typecode);
                            ro_typecode = rightExpr.Emit(codeGenerator);
                            if (ro_typecode == PhpTypeCode.Integer)
                            {
                                returned_typecode = codeGenerator.EmitMethodCall(Methods.Operators.Subtract.Object_Int);
                            }
                            else
                            {
                                codeGenerator.EmitBoxing(ro_typecode);
                                returned_typecode = codeGenerator.EmitMethodCall(Methods.Operators.Subtract.Object_Object);
                            }
                            break;
                    }
					break;

				case Operations.Div:
					//Template: "x / y"   Operators.Divide(x,y)

					lo_typecode = leftExpr.Emit(codeGenerator);
					switch (lo_typecode)
					{
						case PhpTypeCode.Integer:
							codeGenerator.EmitBoxing(rightExpr.Emit(codeGenerator));
							returned_typecode = codeGenerator.EmitMethodCall(Methods.Operators.Divide.Int32_Object);
							break;

						case PhpTypeCode.Double:
                            switch (ro_typecode = rightExpr.Emit(codeGenerator))
                            {
                                case PhpTypeCode.Double:
                                    codeGenerator.IL.Emit(OpCodes.Div);
                                    returned_typecode = PhpTypeCode.Double;
                                    break;
                                default:
                                    codeGenerator.EmitBoxing(ro_typecode);
                                    returned_typecode = codeGenerator.EmitMethodCall(Methods.Operators.Divide.Double_Object);
                                    break;
                            }
                            break;

						default:
							codeGenerator.EmitBoxing(lo_typecode);
							ro_typecode = rightExpr.Emit(codeGenerator);

							switch (ro_typecode)
							{
								case PhpTypeCode.Integer:
									returned_typecode = codeGenerator.EmitMethodCall(Methods.Operators.Divide.Object_Int32);
									break;

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

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

				case Operations.Mul:
					switch (lo_typecode = leftExpr.Emit(codeGenerator))
                    {
                        case PhpTypeCode.Double:
                            // "x * (double)y"
                            // Operators.Multiply((double)x,(object)y)

                            switch (ro_typecode = rightExpr.Emit(codeGenerator))
                            {
                                case PhpTypeCode.Integer:
                                    codeGenerator.IL.Emit(OpCodes.Conv_R8);
                                    goto case PhpTypeCode.Double;   // fallback:
                                case PhpTypeCode.Double:
                                    codeGenerator.IL.Emit(OpCodes.Mul);
                                    returned_typecode = PhpTypeCode.Double;
                                    break;
                                default:
                                    codeGenerator.EmitBoxing(ro_typecode);
                                    returned_typecode = codeGenerator.EmitMethodCall(Methods.Operators.Multiply.Double_Object);
							        break;
                            }
                    
                            break;
                        default:
                            //Template: "x * y"  Operators.Multiply((object)x,y) [overloads]
                            codeGenerator.EmitBoxing(lo_typecode);

                            ro_typecode = rightExpr.Emit(codeGenerator);
					        switch (ro_typecode)
					        {
						        case PhpTypeCode.Integer:
							        returned_typecode = codeGenerator.EmitMethodCall(Methods.Operators.Multiply.Object_Int32);
							        break;

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

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

				case Operations.Mod:
					//Template: "x % y"        Operators.Remainder(x,y)
					codeGenerator.EmitBoxing(leftExpr.Emit(codeGenerator));
					ro_typecode = rightExpr.Emit(codeGenerator);
					switch (ro_typecode)
					{
						case PhpTypeCode.Integer:
							returned_typecode = codeGenerator.EmitMethodCall(Methods.Operators.Remainder.Object_Int32);
							break;

						default:
							codeGenerator.EmitBoxing(ro_typecode);
							returned_typecode = codeGenerator.EmitMethodCall(Methods.Operators.Remainder.Object_Object);
							break;
					}
					break;

				case Operations.ShiftLeft:

					// LOAD Operators.ShiftLeft(box left, box right);
					codeGenerator.EmitBoxing(leftExpr.Emit(codeGenerator));
					codeGenerator.EmitBoxing(rightExpr.Emit(codeGenerator));
					returned_typecode = codeGenerator.EmitMethodCall(Methods.Operators.ShiftLeft);
					break;

				case Operations.ShiftRight:

					// LOAD Operators.ShiftRight(box left, box right);
					codeGenerator.EmitBoxing(leftExpr.Emit(codeGenerator));
					codeGenerator.EmitBoxing(rightExpr.Emit(codeGenerator));
					returned_typecode = codeGenerator.EmitMethodCall(Methods.Operators.ShiftRight);
					break;

				#endregion

				#region Boolean and Bitwise Operations

				case Operations.And:
					returned_typecode = EmitBinaryBooleanOperation(codeGenerator, true);
					break;

				case Operations.Or:
					returned_typecode = EmitBinaryBooleanOperation(codeGenerator, false);
					break;

				case Operations.Xor:

					// LOAD <(bool) leftSon> == <(bool) rightSon>;
					codeGenerator.EmitConversion(leftExpr, PhpTypeCode.Boolean);
					codeGenerator.EmitConversion(rightExpr, PhpTypeCode.Boolean);
                    codeGenerator.IL.Emit(OpCodes.Ceq);

                    codeGenerator.IL.Emit(OpCodes.Ldc_I4_0);
                    codeGenerator.IL.Emit(OpCodes.Ceq);

					returned_typecode = PhpTypeCode.Boolean;
					break;

				case Operations.BitAnd:
					returned_typecode = EmitBitOperation(codeGenerator, Operators.BitOp.And);
					break;

				case Operations.BitOr:
					returned_typecode = EmitBitOperation(codeGenerator, Operators.BitOp.Or);
					break;

				case Operations.BitXor:
					returned_typecode = EmitBitOperation(codeGenerator, Operators.BitOp.Xor);
					break;

				#endregion

				#region Comparing Operations

				case Operations.Equal:

					// LOAD PhpComparer.Default.CompareEq
					returned_typecode = EmitComparison(codeGenerator, true);
					break;

				case Operations.NotEqual:

					// LOAD PhpComparer.Default.CompareEq == false
					EmitComparison(codeGenerator, true);
                    codeGenerator.IL.Emit(OpCodes.Ldc_I4_0);
                    codeGenerator.IL.Emit(OpCodes.Ceq);

					returned_typecode = PhpTypeCode.Boolean;
					break;

				case Operations.GreaterThan:

					// LOAD PhpComparer.Default.Compare > 0;
					EmitComparison(codeGenerator, false);
                    codeGenerator.IL.Emit(OpCodes.Ldc_I4_0);
                    codeGenerator.IL.Emit(OpCodes.Cgt);

					returned_typecode = PhpTypeCode.Boolean;
					break;

				case Operations.LessThan:

					// LOAD PhpComparer.Default.Compare < 0;
					EmitComparison(codeGenerator, false);
                    codeGenerator.IL.Emit(OpCodes.Ldc_I4_0);
                    codeGenerator.IL.Emit(OpCodes.Clt);

					returned_typecode = PhpTypeCode.Boolean;
					break;

				case Operations.GreaterThanOrEqual:

					// LOAD PhpComparer.Default.Compare >= 0 (not less than)
					EmitComparison(codeGenerator, false);
                    codeGenerator.IL.Emit(OpCodes.Ldc_I4_0);
                    codeGenerator.IL.Emit(OpCodes.Clt);
                    codeGenerator.IL.Emit(OpCodes.Ldc_I4_0);
                    codeGenerator.IL.Emit(OpCodes.Ceq);

					returned_typecode = PhpTypeCode.Boolean;
					break;

				case Operations.LessThanOrEqual:

					// LOAD PhpComparer.Default.Compare >= 0 (not greater than)
					EmitComparison(codeGenerator, false);
                    codeGenerator.IL.Emit(OpCodes.Ldc_I4_0);
                    codeGenerator.IL.Emit(OpCodes.Cgt);
                    codeGenerator.IL.Emit(OpCodes.Ldc_I4_0);
                    codeGenerator.IL.Emit(OpCodes.Ceq);

					returned_typecode = PhpTypeCode.Boolean;
					break;

				case Operations.Identical:

					// LOAD Operators.StrictEquality(box left,box right);
					codeGenerator.EmitBoxing(leftExpr.Emit(codeGenerator));
					codeGenerator.EmitBoxing(rightExpr.Emit(codeGenerator));
                    codeGenerator.IL.Emit(OpCodes.Call, Methods.Operators.StrictEquality);

					returned_typecode = PhpTypeCode.Boolean;
					break;

				case Operations.NotIdentical:

					// LOAD Operators.StrictEquality(box left,box right) == false;
					codeGenerator.EmitBoxing(leftExpr.Emit(codeGenerator));
					codeGenerator.EmitBoxing(rightExpr.Emit(codeGenerator));
                    codeGenerator.IL.Emit(OpCodes.Call, Methods.Operators.StrictEquality);
                    codeGenerator.IL.Emit(OpCodes.Ldc_I4_0);
                    codeGenerator.IL.Emit(OpCodes.Ceq);

					returned_typecode = PhpTypeCode.Boolean;
					break;

				#endregion

				case Operations.Concat:
					returned_typecode = ConcatEx.EmitConcat(codeGenerator, leftExpr, rightExpr);
					break;

				default:
					throw null;
			}

			switch (access)
			{
				case AccessType.Read:
					// Result is read, do nothing.
					break;

				case AccessType.None:
					// Result is not read, pop the result
                    codeGenerator.IL.Emit(OpCodes.Pop);
					returned_typecode = PhpTypeCode.Void;
					break;
			}

			return returned_typecode;
		}
Example #2
0
		/// <include file='Doc/Nodes.xml' path='doc/method[@name="Emit"]/*'/>
		internal override PhpTypeCode Emit(CodeGenerator/*!*/ codeGenerator)
		{
			Debug.Assert(access == AccessType.Read || access == AccessType.None);
			Statistics.AST.AddNode("UnaryEx");

			ILEmitter il = codeGenerator.IL;

			PhpTypeCode returned_typecode, o_typecode;

			switch (operation)
			{
				case Operations.AtSign:	// special arrangement
					// Template:
					//		context.DisableErrorReporting();
					//		s;
					//		context.EnableErrorReporting();
					codeGenerator.EmitLoadScriptContext();
					il.Emit(OpCodes.Call, Methods.ScriptContext.DisableErrorReporting);
					returned_typecode = expr.Emit(codeGenerator);
					codeGenerator.EmitLoadScriptContext();
					il.Emit(OpCodes.Call, Methods.ScriptContext.EnableErrorReporting);
					break;

				case Operations.BitNegation:
					//Template: "~x" Operators.BitNot(x)                                     
					codeGenerator.EmitBoxing(expr.Emit(codeGenerator));
					il.Emit(OpCodes.Call, Methods.Operators.BitNot);
					returned_typecode = PhpTypeCode.Object;
					break;

				case Operations.Clone:
					// Template: clone x        Operators.Clone(x,DTypeDesc,ScriptContext)
					codeGenerator.EmitBoxing(expr.Emit(codeGenerator));
					codeGenerator.EmitLoadClassContext();
					codeGenerator.EmitLoadScriptContext();
					il.Emit(OpCodes.Call, Methods.Operators.Clone);
					returned_typecode = PhpTypeCode.Object;
					break;

				case Operations.LogicNegation:
					//Template: "!x"  !Convert.ObjectToBoolean(x);                              
                    if (((returned_typecode = expr.Emit(codeGenerator)) != PhpTypeCode.Boolean))
                    {
                        codeGenerator.EmitBoxing(returned_typecode);
                        il.Emit(OpCodes.Call, Methods.Convert.ObjectToBoolean);
                    }
					il.Emit(OpCodes.Ldc_I4_0);
					il.Emit(OpCodes.Ceq);
					returned_typecode = PhpTypeCode.Boolean;
					break;

				case Operations.Minus:
					//Template: "-x"  Operators.Minus(x)
                    switch (o_typecode = expr.Emit(codeGenerator))
                    {
                        case PhpTypeCode.Double:
                            il.Emit(OpCodes.Neg);
                            returned_typecode = PhpTypeCode.Double;
                            break;
                        default:
					        codeGenerator.EmitBoxing(o_typecode);
                            returned_typecode = codeGenerator.EmitMethodCall(Methods.Operators.Minus);
					        break;
                    }
                    break;

				case Operations.ObjectCast:
					//Template: "(object)x"   Convert.ObjectToDObject(x,ScriptContext)
					codeGenerator.EmitBoxing(expr.Emit(codeGenerator));
					codeGenerator.EmitLoadScriptContext();
					il.Emit(OpCodes.Call, Methods.Convert.ObjectToDObject);
					returned_typecode = PhpTypeCode.Object;
					break;

				case Operations.Plus:
					//Template: "+x"  Operators.Plus(x)
					codeGenerator.EmitBoxing(expr.Emit(codeGenerator));
					il.Emit(OpCodes.Call, Methods.Operators.Plus);
					returned_typecode = PhpTypeCode.Object;
					break;

				case Operations.Print:
					codeGenerator.EmitEcho(this.expr);
					// Always returns 1
					il.Emit(OpCodes.Ldc_I4_1);
					returned_typecode = PhpTypeCode.Integer;
					break;

				case Operations.BoolCast:
					//Template: "(bool)x"     Convert.ObjectToBoolean(x)
                    if (((returned_typecode = expr.Emit(codeGenerator)) != PhpTypeCode.Boolean))
                    {
                        codeGenerator.EmitBoxing(returned_typecode);
                        il.Emit(OpCodes.Call, Methods.Convert.ObjectToBoolean);
                        returned_typecode = PhpTypeCode.Boolean;
                    }
					break;

				case Operations.Int8Cast:
				case Operations.Int16Cast:
				case Operations.Int32Cast:
				case Operations.UInt8Cast:
				case Operations.UInt16Cast:
					// CALL int Convert.ObjectToInteger(<expr>)
                    o_typecode = expr.Emit(codeGenerator);
                    if (o_typecode != PhpTypeCode.Integer)
                    {
                        codeGenerator.EmitBoxing(o_typecode);
                        il.Emit(OpCodes.Call, Methods.Convert.ObjectToInteger);
                    }

					// CONV for unsigned:
					switch (operation)
					{
						case Operations.UInt8Cast: il.Emit(OpCodes.Conv_U1); il.Emit(OpCodes.Conv_I4); break;
						case Operations.UInt16Cast: il.Emit(OpCodes.Conv_U2); il.Emit(OpCodes.Conv_I4); break;
					}

					returned_typecode = PhpTypeCode.Integer;
					break;

				case Operations.UInt64Cast:
				case Operations.UInt32Cast:
				case Operations.Int64Cast:
					// CALL long Convert.ObjectToLongInteger(<expr>)
                    o_typecode = expr.Emit(codeGenerator);
                    if (o_typecode != PhpTypeCode.LongInteger)
                    {
                        codeGenerator.EmitBoxing(o_typecode);
                        il.Emit(OpCodes.Call, Methods.Convert.ObjectToLongInteger);
                    }

					// CONV for unsigned:
					switch (operation)
					{
						case Operations.UInt32Cast: il.Emit(OpCodes.Conv_U4); il.Emit(OpCodes.Conv_I8); break;
						case Operations.UInt64Cast: il.Emit(OpCodes.Conv_U8); il.Emit(OpCodes.Conv_I8); break;
					}

					returned_typecode = PhpTypeCode.LongInteger;
					break;

				case Operations.DecimalCast:
				case Operations.DoubleCast:
				case Operations.FloatCast:
					// CALL double Convert.ObjectToDouble(<expr>)
                    o_typecode = expr.Emit(codeGenerator);
                    if (o_typecode != PhpTypeCode.Double)
                    {
                        codeGenerator.EmitBoxing(o_typecode);
                        il.Emit(OpCodes.Call, Methods.Convert.ObjectToDouble);
                    }
					returned_typecode = PhpTypeCode.Double;
					break;

				case Operations.UnicodeCast: // TODO
				case Operations.StringCast:
                    if ((returned_typecode = expr.Emit(codeGenerator)) != PhpTypeCode.String)
                    {
                        codeGenerator.EmitBoxing(returned_typecode);
                        //codeGenerator.EmitLoadClassContext();
                        il.Emit(OpCodes.Call, Methods.Convert.ObjectToString);
                        returned_typecode = PhpTypeCode.String;
                    }
					break;

                case Operations.BinaryCast:
                    if ((returned_typecode = expr.Emit(codeGenerator)) != PhpTypeCode.PhpBytes)
                    {
                        codeGenerator.EmitBoxing(returned_typecode);
                        //codeGenerator.EmitLoadClassContext();
                        il.Emit(OpCodes.Call, Methods.Convert.ObjectToPhpBytes);
                        returned_typecode = PhpTypeCode.PhpBytes;
                    }
                    break;

				case Operations.ArrayCast:
					//Template: "(array)x"   Convert.ObjectToArray(x)
                    o_typecode = expr.Emit(codeGenerator);
                    if (o_typecode != PhpTypeCode.PhpArray)
                    {
                        codeGenerator.EmitBoxing(o_typecode);
                        il.Emit(OpCodes.Call, Methods.Convert.ObjectToPhpArray);
                    }
					returned_typecode = PhpTypeCode.PhpArray;
					break;

				case Operations.UnsetCast:
					// Template: "(unset)x"  null
					il.Emit(OpCodes.Ldnull);
					returned_typecode = PhpTypeCode.Object;
					break;

				default:
					Debug.Assert(false, "illegal type of operation!");
					returned_typecode = PhpTypeCode.Void;
					break;
			}

			switch (access)
			{
				case AccessType.Read:
					// do nothing
					break;
				case AccessType.None:
					// pop operation's result value from stack
					if (returned_typecode != PhpTypeCode.Void)
						il.Emit(OpCodes.Pop);
					return PhpTypeCode.Void;
			}

			return returned_typecode;
		}
Example #3
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;
		}