/// <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; }
/// <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; }
/// <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; }