/// <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; }
/// <summary> /// Emit IL instructions that load the value of array item at the stack and make a copy /// of it if necessary. /// </summary> internal override PhpTypeCode EmitValue(CodeGenerator/*!*/ codeGenerator) { Debug.Assert(valueExpr != null); Statistics.AST.AddNode("Array.ValueItem"); codeGenerator.EmitBoxing(valueExpr.Emit(codeGenerator)); codeGenerator.EmitVariableCopy(CopyReason.Assigned, valueExpr); return PhpTypeCode.Object; }
private void EmitAssignListArray(CodeGenerator codeGenerator, List<Expression> vals, LocalBuilder o1) { // // the array is on the top of the evaluation stack, value will be kept, must be duplicated to be used // // Process in the reverse order ! for (int i = vals.Count - 1; i >= 0; i--) { if (vals[i] == null) continue; // push the array item onto the stack // LOAD array.GetArrayItem(i,false) codeGenerator.IL.Emit(OpCodes.Dup); // copy of the array codeGenerator.IL.Emit(OpCodes.Ldc_I4, i); // i codeGenerator.IL.Emit(OpCodes.Ldc_I4_0); // false (!quiet) codeGenerator.IL.Emit(OpCodes.Callvirt, Methods.PhpArray.GetArrayItem_Int32); // assign the item from the stack into vals[i] if (vals[i] is VariableUse) { // o1 = stack[0] codeGenerator.IL.Stloc(o1); // store the value into local variable o1 // PREPARE <variable>: codeGenerator.ChainBuilder.Create(); vals[i].Emit(codeGenerator); // LOAD o1 codeGenerator.IL.Ldloc(o1); // LOAD PhpVariable.Copy(STACK,CopyReason.Assigned) codeGenerator.EmitVariableCopy(CopyReason.Assigned, null); // STORE <variable>: (vals[i] as VariableUse).EmitAssign(codeGenerator); codeGenerator.ChainBuilder.End(); } else if (vals[i] is ListEx) { EmitAssignList(codeGenerator, (vals[i] as ListEx).LValues, o1); codeGenerator.IL.Emit(OpCodes.Pop); // removes used value from the stack } else { codeGenerator.IL.Emit(OpCodes.Pop); // removes used value from the stack Debug.Fail("Unsupported list argument of type " + vals[i].GetType().ToString()); } } }