/// <summary> /// Assigns null into given lvalues recursively. /// </summary> /// <param name="codeGenerator"></param> /// <param name="vals"></param> private static void EmitAssignListNulls(CodeGenerator codeGenerator, List <Expression> vals) { // clear lvalues recursively for (int i = 0; i < vals.Count; ++i) { if (vals[i] == null) { continue; } if (vals[i] is VariableUse) { // Prepare stack for writing result... codeGenerator.ChainBuilder.Create(); (vals[i] as VariableUse).Emit(codeGenerator); codeGenerator.IL.Emit(OpCodes.Ldnull); // Store result VariableUseHelper.EmitAssign((VariableUse)vals[i], codeGenerator); codeGenerator.ChainBuilder.End(); } else if (vals[i] is ListEx) { EmitAssignListNulls(codeGenerator, (vals[i] as ListEx).LValues); } else { Debug.Fail("Unsupported list argument of type " + vals[i].GetType().ToString()); } } }
private static 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>: VariableUseHelper.EmitAssign((VariableUse)vals[i], 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()); } } }
public override PhpTypeCode Emit(IssetEx node, CodeGenerator codeGenerator) { Debug.Assert(access == AccessType.None || access == AccessType.Read); Statistics.AST.AddNode("IssetEx"); ILEmitter il = codeGenerator.IL; codeGenerator.ChainBuilder.Create(); codeGenerator.ChainBuilder.QuietRead = true; var vars = node.VarList; if (vars.Count == 1) { codeGenerator.EmitBoxing(VariableUseHelper.EmitIsset(vars[0], codeGenerator, false)); // Compare the result with "null" il.CmpNotNull(); } else { // Define labels Label f_label = il.DefineLabel(); Label x_label = il.DefineLabel(); // Get first variable codeGenerator.EmitBoxing(VariableUseHelper.EmitIsset(vars[0], codeGenerator, false)); // Compare the result with "null" il.CmpNotNull(); // Process following variables and include branching for (int i = 1; i < vars.Count; i++) { il.Emit(OpCodes.Brfalse, f_label); codeGenerator.EmitBoxing(VariableUseHelper.EmitIsset(vars[i], codeGenerator, false)); // Compare the result with "null" codeGenerator.IL.CmpNotNull(); } il.Emit(OpCodes.Br, x_label); il.MarkLabel(f_label, true); il.Emit(OpCodes.Ldc_I4_0); il.MarkLabel(x_label, true); } codeGenerator.ChainBuilder.End(); if (access == AccessType.None) { il.Emit(OpCodes.Pop); return(PhpTypeCode.Void); } return(PhpTypeCode.Boolean); }
internal override void Emit(UnsetStmt node, CodeGenerator codeGenerator) { Statistics.AST.AddNode("UnsetStmt"); codeGenerator.MarkSequencePoint(node.Span); foreach (VariableUse variable in node.VarList) { codeGenerator.ChainBuilder.Create(); codeGenerator.ChainBuilder.QuietRead = true; VariableUseHelper.EmitUnset(variable, codeGenerator); codeGenerator.ChainBuilder.End(); } }
public PhpTypeCode EmitAssign(ForeachVar /*!*/ node, CodeGenerator codeGenerator) { // Object (or PhpReference) is on top of evaluation stack var varuse = node.Variable; if (varuse != null) { return(VariableUseHelper.EmitAssign(varuse, codeGenerator)); } else { var listex = node.List; if (listex != null) { return(listex.NodeCompiler <ListExCompiler>().EmitAssign(listex, codeGenerator)); } else { throw new InvalidOperationException(); } } }
public override PhpTypeCode Emit(AssignEx node, CodeGenerator codeGenerator) { Debug.Assert(access == AccessType.None || access == AccessType.Read || access == AccessType.ReadRef || access == AccessType.ReadUnknown); Statistics.AST.AddNode("Assign.Ref"); //ChainBuilder.RefErrorLabelInfo labelInfo; // Strict Standards: Only variables should be assigned by reference /*if (rvalue is FunctionCall)//TODO: only variables (but also variables given by function call return value!) * { * il.LdcI4( (int)PhpError.Strict ); * il.Emit(OpCodes.Ldstr, CoreResources.GetString("only_vars_assign ed_by_ref")); * codeGenerator.EmitPhpException(il,Methods.PhpException.Throw); * }*/ // PREPARE: codeGenerator.ChainBuilder.Create(); node.LValue.Emit(codeGenerator); // LOAD <right hand side>: codeGenerator.ChainBuilder.Create(); ((RefAssignEx)node).RValue.Emit(codeGenerator); codeGenerator.ChainBuilder.End(); PhpTypeCode result; // Dup source value if assignment is read switch (access) { case AccessType.Read: case AccessType.ReadUnknown: case AccessType.ReadRef: { // DUP codeGenerator.IL.Emit(OpCodes.Dup); // STORE tmp codeGenerator.IL.Stloc(codeGenerator.IL.GetAssignmentLocalRef()); // STORE prepared,result VariableUseHelper.EmitAssign(node.LValue, codeGenerator); // LOAD DEREF tmp codeGenerator.IL.Ldloc(codeGenerator.IL.GetAssignmentLocalRef()); if (access == AccessType.Read) { codeGenerator.IL.Emit(OpCodes.Ldfld, Fields.PhpReference_Value); result = PhpTypeCode.Object; } else { result = PhpTypeCode.PhpReference; } break; } case AccessType.None: VariableUseHelper.EmitAssign(node.LValue, codeGenerator); result = PhpTypeCode.Void; break; default: throw new InvalidOperationException(); //result = PhpTypeCode.Invalid; //break; } codeGenerator.ChainBuilder.End(); return(result); }
/// <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> public override PhpTypeCode Emit(AssignEx node, CodeGenerator codeGenerator) { Debug.Assert(access == AccessType.Read || access == AccessType.None || access == AccessType.ReadRef || access == AccessType.ReadUnknown); Statistics.AST.AddNode("Assign.Value"); AccessType old_selector = codeGenerator.AccessSelector; codeGenerator.ChainBuilder.Create(); PhpTypeCode result; if (node.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; node.lvalue.Emit(codeGenerator); codeGenerator.AccessSelector = AccessType.None; PhpTypeCode src_type_code = EmitSourceValRead((ValueAssignEx)node, 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, ((ValueAssignEx)node).rvalue); } } else { // PREPARE a: codeGenerator.AccessSelector = AccessType.Write; node.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((ValueAssignEx)node, codeGenerator); var rvalue_tmp = codeGenerator.IL.GetTemporaryLocal(PhpTypeCodeEnum.ToType(right_type), false); codeGenerator.IL.Emit(OpCodes.Stloc, rvalue_tmp); EmitDestVarRead(node, codeGenerator); codeGenerator.IL.Emit(OpCodes.Ldloc, rvalue_tmp); codeGenerator.IL.ReturnTemporaryLocal(rvalue_tmp); switch (node.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.AssignPow: codeGenerator.EmitBoxing(right_type); result = codeGenerator.EmitMethodCall(Methods.Operators.Pow.Object_Object); 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); codeGenerator.IL.Emit(OpCodes.Ldc_I4, (int)Operators.BitOp.And); result = codeGenerator.EmitMethodCall(Methods.Operators.BitOperation); break; case Operations.AssignOr: codeGenerator.EmitBoxing(right_type); codeGenerator.IL.Emit(OpCodes.Ldc_I4, (int)Operators.BitOp.Or); result = codeGenerator.EmitMethodCall(Methods.Operators.BitOperation); break; case Operations.AssignXor: codeGenerator.EmitBoxing(right_type); codeGenerator.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: throw new InvalidOperationException(); } codeGenerator.IL.EmitBoxing(result); } switch (access) { case AccessType.Read: { // DUP codeGenerator.IL.Emit(OpCodes.Dup); // STORE tmp codeGenerator.IL.Stloc(codeGenerator.IL.GetAssignmentLocal()); // STORE prepared, result codeGenerator.AccessSelector = AccessType.Write; result = VariableUseHelper.EmitAssign(node.LValue, codeGenerator); codeGenerator.AccessSelector = AccessType.None; Debug.Assert(result == PhpTypeCode.Void); // LOAD result codeGenerator.IL.Ldloc(codeGenerator.IL.GetAssignmentLocal()); result = PhpTypeCode.Object; break; } case AccessType.ReadRef: case AccessType.ReadUnknown: // STORE prepared,result codeGenerator.AccessSelector = AccessType.Write; result = VariableUseHelper.EmitAssign(node.LValue, codeGenerator); codeGenerator.AccessSelector = AccessType.None; Debug.Assert(result == PhpTypeCode.Void); // loads a reference on the LHS variable: codeGenerator.AccessSelector = access; codeGenerator.ChainBuilder.Create(); result = node.LValue.Emit(codeGenerator); codeGenerator.ChainBuilder.EndRef(); codeGenerator.AccessSelector = AccessType.None; break; case AccessType.None: // STORE a: codeGenerator.AccessSelector = AccessType.Write; result = VariableUseHelper.EmitAssign(node.LValue, 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); }