/// <include file='Doc/Nodes.xml' path='doc/method[@name="Emit"]/*'/> internal override PhpTypeCode Emit(CodeGenerator codeGenerator) { Statistics.AST.AddNode("ListEx"); Debug.Assert(access == AccessType.Read || access == AccessType.None); Debug.Assert(RValue != null); // the root of the lists structure must have RValue assigned. list(whatever) = RValue codeGenerator.EmitBoxing(RValue.Emit(codeGenerator)); // put object on the top of the stack LocalBuilder o1 = codeGenerator.IL.GetTemporaryLocal(Types.Object[0]); // temporary variable for object to be copied EmitAssignList(codeGenerator, LValues, o1); // assign particular elements of the list, using the array from the stack // return temporary local codeGenerator.IL.ReturnTemporaryLocal(o1); // the original top of the stack is replaced with the instance of array or null if (access == AccessType.Read) { return PhpTypeCode.PhpArray; // return the top of the stack (null or array) } else { codeGenerator.IL.Emit(OpCodes.Pop); // remove the top of the stack, not used return PhpTypeCode.Void; } }
/// <include file='Doc/Nodes.xml' path='doc/method[@name="Emit"]/*'/> internal override PhpTypeCode Emit(CodeGenerator/*!*/ codeGenerator) { Statistics.AST.AddNode("InstanceOfEx"); // emits load of expression value on the stack: codeGenerator.EmitBoxing(expression.Emit(codeGenerator)); if (classNameRef.ResolvedType != null && typeArgsResolved) { // type is resolvable (doesn't mean known) // classNameRef.ResolvedType.EmitInstanceOf(codeGenerator, null); } else { // type is unresolvable (there is some variable or the type is a generic parameter) // codeGenerator.EmitInstanceOfOperator(null, classNameRef, null); } if (access == AccessType.None) { codeGenerator.IL.Emit(OpCodes.Pop); return PhpTypeCode.Void; } else { return PhpTypeCode.Boolean; } }
/// <summary> /// Emits strict equality operation. /// </summary> /// <param name="codeGenerator">A code generator.</param> /// <returns>A type code of the result (boolean).</returns> private PhpTypeCode EmitStrictEquality(CodeGenerator codeGenerator) { if (IsEmptyArrayEx(leftExpr)) { EmitEmptyArrayStrictEquality(codeGenerator, rightExpr); } else if (IsEmptyArrayEx(rightExpr)) { EmitEmptyArrayStrictEquality(codeGenerator, leftExpr); } else { // 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); } return PhpTypeCode.Boolean; }
/// <include file='Doc/Nodes.xml' path='doc/method[@name="Emit"]/*'/> internal override void Emit(CodeGenerator/*!*/ codeGenerator) { Statistics.AST.AddNode("SwitchStmt"); ILEmitter il = codeGenerator.IL; // Note: // SwitchStmt is now implemented in the most general (and unefficient) way. The whole switch // is understood as a series of if-elseif-else statements. Label exit_label = il.DefineLabel(); bool fall_through = false; Label fall_through_label = il.DefineLabel(); Label last_default_label = il.DefineLabel(); DefaultItem last_default = GetLastDefaultItem(); LocalBuilder branch_to_lastdefault = null; if (last_default != null) { branch_to_lastdefault = il.DeclareLocal(Types.Bool[0]); il.LdcI4(0); il.Stloc(branch_to_lastdefault); } codeGenerator.BranchingStack.BeginLoop(exit_label, exit_label, codeGenerator.ExceptionBlockNestingLevel); // marks a sequence point containing the discriminator evaluation: codeGenerator.MarkSequencePoint( switchValue.Position.FirstLine, switchValue.Position.FirstColumn, switchValue.Position.LastLine, switchValue.Position.LastColumn + 1); // Evaluate condition value and store the result into local variable codeGenerator.EmitBoxing(switchValue.Emit(codeGenerator)); LocalBuilder condition_value = il.DeclareLocal(Types.Object[0]); il.Stloc(condition_value); foreach (SwitchItem item in switchItems) { item.MarkSequencePoint(codeGenerator); // switch item is either CaseItem ("case xxx:") or DefaultItem ("default") item: CaseItem case_item = item as CaseItem; if (case_item != null) { Label false_label = il.DefineLabel(); // PhpComparer.Default.CompareEq(<switch expr. value>,<case value>); /*changed to static method*/ //il.Emit(OpCodes.Ldsfld, Fields.PhpComparer_Default); codeGenerator.EmitCompareEq( cg => { cg.IL.Ldloc(condition_value); return PhpTypeCode.Object; }, cg => case_item.EmitCaseValue(cg)); // IF (!STACK) GOTO false_label; il.Emit(OpCodes.Brfalse, false_label); if (fall_through == true) { il.MarkLabel(fall_through_label, true); fall_through = false; } case_item.EmitStatements(codeGenerator); if (fall_through == false) { fall_through_label = il.DefineLabel(); fall_through = true; } il.Emit(OpCodes.Br, fall_through_label); il.MarkLabel(false_label, true); } else { DefaultItem default_item = (DefaultItem)item; // Only the last default branch defined in source code is used. // So skip default while testing "case" items at runtime. Label false_label = il.DefineLabel(); il.Emit(OpCodes.Br, false_label); if (default_item == last_default) { il.MarkLabel(last_default_label, false); } if (fall_through == true) { il.MarkLabel(fall_through_label, true); fall_through = false; } default_item.EmitStatements(codeGenerator); if (fall_through == false) { fall_through_label = il.DefineLabel(); fall_through = true; } il.Emit(OpCodes.Br, fall_through_label); il.MarkLabel(false_label, true); } } // If no case branch matched, branch to last default case if any is defined if (last_default != null) { // marks a sequence point containing the condition evaluation or skip of the default case: codeGenerator.MarkSequencePoint( last_default.Position.FirstLine, last_default.Position.FirstColumn, last_default.Position.LastLine, last_default.Position.LastColumn + 1); Debug.Assert(branch_to_lastdefault != null); Label temp = il.DefineLabel(); // IF (!branch_to_lastdefault) THEN il.Ldloc(branch_to_lastdefault); il.LdcI4(0); il.Emit(OpCodes.Bne_Un, temp); if (true) { // branch_to_lastdefault = TRUE; il.LdcI4(1); il.Stloc(branch_to_lastdefault); // GOTO last_default_label; il.Emit(OpCodes.Br, last_default_label); } il.MarkLabel(temp, true); // END IF; il.ForgetLabel(last_default_label); } if (fall_through == true) { il.MarkLabel(fall_through_label, true); } il.MarkLabel(exit_label); codeGenerator.BranchingStack.EndLoop(); il.ForgetLabel(exit_label); }
/// <summary> /// Emits code to load <see cref="PhpRuntimeChain"/> onto an evaluation stack. Supports operators chaining. /// </summary> /// <param name="codeGenerator"></param> private PhpTypeCode EmitNodeReadUnknown(CodeGenerator codeGenerator) { ChainBuilder chain = codeGenerator.ChainBuilder; PhpTypeCode result = PhpTypeCode.PhpRuntimeChain; if (chain.IsArrayItem) { // 3: a_[x]_[x] chain.Lengthen(); // for [] chain.EmitRTChainAddItem(this); return result; } // 1,2,4,5,6,7 if (chain.IsMember) { // 4, 5 if (this.isMemberOf != null) { // 5: ...->a[]->... // Lengthen chain for isMemberOf chain.Lengthen(); // for hop over -> PhpTypeCode res = isMemberOf.Emit(codeGenerator); if (res != PhpTypeCode.PhpRuntimeChain) { codeGenerator.EmitBoxing(res); chain.EmitCreateRTChain(); } // Lengthen chain for own [] chain.Lengthen(); chain.IsArrayItem = true; chain.IsLastMember = false; chain.EmitRTChainAddItem(this); chain.IsArrayItem = false; return result; } // 4: a[x]->... // Lengthen chain for itself chain.Lengthen(); // for own [] chain.IsArrayItem = true; chain.IsLastMember = true; chain.EmitRTChainAddItem(this); chain.IsArrayItem = false; return result; } // 1, 2, 6, 7 if (this.isMemberOf != null) { // 6 , 7: ...->a[]_[]_ , ...->a_[]_ bool quiet_read = chain.QuietRead; chain.Create(); chain.Begin(); chain.QuietRead = quiet_read; chain.Lengthen(); // for hop over -> PhpTypeCode res = isMemberOf.Emit(codeGenerator); if (res != PhpTypeCode.PhpRuntimeChain) { codeGenerator.EmitBoxing(res); chain.EmitCreateRTChain(); } chain.IsArrayItem = true; chain.IsLastMember = false; chain.EmitRTChainAddItem(this); chain.IsArrayItem = false; chain.End(); return result; } // 1, 2 if (array is ItemUse || array is DirectStFldUse || array is IndirectStFldUse /* ??? */) { // 2: a[]_[]_ bool quiet_read = chain.QuietRead; chain.Create(); chain.Begin(); chain.QuietRead = quiet_read; chain.IsArrayItem = true; chain.IsLastMember = true; chain.EmitRTChainAddItem(this); chain.IsArrayItem = false; chain.End(); return result; } // 1: a_[x]_ chain.IsArrayItem = true; chain.IsLastMember = true; chain.EmitRTChainAddItem(this); chain.IsArrayItem = false; return result; }
/// <summary> /// Emits bit operation <see cref="leftExpr"/> OP <see cref="rightExpr"/>. /// </summary> /// <param name="codeGenerator">A code generator.</param> /// <param name="op">The operation.</param> /// <returns>A type code of the result.</returns> private PhpTypeCode EmitBitOperation(CodeGenerator/*!*/ codeGenerator, Operators.BitOp op) { ILEmitter il = codeGenerator.IL; // LOAD Operators.BitOperation(box <leftSon>, box <rightSon>); codeGenerator.EmitBoxing(leftExpr.Emit(codeGenerator)); codeGenerator.EmitBoxing(rightExpr.Emit(codeGenerator)); il.Emit(OpCodes.Ldc_I4, (int)op); il.Emit(OpCodes.Call, Methods.Operators.BitOperation); return PhpTypeCode.Object; }
internal void EmitLoadArgsOnEvalStack(CallSignature/*!*/node, CodeGenerator/*!*/ codeGenerator, PhpRoutine/*!*/ routine) { ILEmitter il = codeGenerator.IL; int mandatory_count = (routine.Signature != null) ? routine.Signature.MandatoryParamCount : 0; int formal_count = (routine.Signature != null) ? routine.Signature.ParamCount : 0; int actual_count = node.Parameters.Count; PhpTypeCode param_type; // loads all actual parameters which are not superfluous: for (int i = 0; i < Math.Min(actual_count, formal_count); i++) { var p = node.Parameters[i]; codeGenerator.EmitBoxing(param_type = p.NodeCompiler<ActualParamCompiler>().Emit(p, codeGenerator)); // Actual param emitter should emit "boxing" to a reference if its access type is ReadRef. // That's why no operation is needed here and references should match. Debug.Assert((routine.Signature == null || routine.Signature.IsAlias(i)) == (param_type == PhpTypeCode.PhpReference)); } // loads missing mandatory arguments: for (int i = actual_count; i < mandatory_count; i++) { // CALL PhpException.MissingArgument(<i+1>,<name>); il.LdcI4(i + 1); il.Emit(OpCodes.Ldstr, routine.FullName); codeGenerator.EmitPhpException(Methods.PhpException.MissingArgument); // LOAD null; if (routine.Signature.IsAlias(i)) il.Emit(OpCodes.Newobj, Constructors.PhpReference_Void); else il.Emit(OpCodes.Ldnull); } // loads missing optional arguments: for (int i = Math.Max(mandatory_count, actual_count); i < formal_count; i++) { // LOAD Arg.Default; il.Emit(OpCodes.Ldsfld, Fields.Arg_Default); } }
/// <summary> /// Emits IL instructions that load actual parameters and optionally add a new stack frame to /// current <see cref="PHP.Core.ScriptContext.Stack"/>. /// </summary> /// <param name="codeGenerator">Code generator.</param> /// <remarks> /// Nothing is expected on the evaluation stack. Nothing is left on the evaluation stack. /// </remarks> internal void EmitLoadOnPhpStack(CodeGenerator/*!*/ codeGenerator) { List<ActualParam> parameters = this.parameters; List<TypeRef> genericParams = this.genericParams; PhpStackBuilder.EmitAddFrame(codeGenerator.IL, codeGenerator.ScriptContextPlace, genericParams.Count, parameters.Count, delegate(ILEmitter il, int i) { // generic arguments: genericParams[i].EmitLoadTypeDesc(codeGenerator, ResolveTypeFlags.UseAutoload | ResolveTypeFlags.ThrowErrors); }, delegate(ILEmitter il, int i) { // regular arguments: codeGenerator.EmitBoxing(parameters[i].Emit(codeGenerator)); } ); }
/// <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; }
/// <include file='Doc/Nodes.xml' path='doc/method[@name="Emit"]/*'/> internal override PhpTypeCode Emit(CodeGenerator codeGenerator) { Statistics.AST.AddNode("TernaryEx"); Debug.Assert(access == AccessType.Read || access == AccessType.None); Label end_label = codeGenerator.IL.DefineLabel(); if (trueExpr != null) // standard ternary operator { Label else_label = codeGenerator.IL.DefineLabel(); // IF (<(bool) condition>) THEN codeGenerator.EmitConversion(condExpr, PhpTypeCode.Boolean); codeGenerator.IL.Emit(OpCodes.Brfalse, else_label); { codeGenerator.EmitBoxing(trueExpr.Emit(codeGenerator)); codeGenerator.IL.Emit(OpCodes.Br, end_label); } // ELSE codeGenerator.IL.MarkLabel(else_label, true); { codeGenerator.EmitBoxing(falseExpr.Emit(codeGenerator)); } } else { // ternary shortcut: var il = codeGenerator.IL; // condExpr ?? rightExpr il.EmitBoxing(condExpr.Emit(codeGenerator)); // IF (<stack>): il.Emit(OpCodes.Dup); il.Emit(OpCodes.Call, Methods.Convert.ObjectToBoolean); codeGenerator.IL.Emit(OpCodes.Brtrue, end_label); // ELSE: { il.Emit(OpCodes.Pop); il.EmitBoxing(falseExpr.Emit(codeGenerator)); } } // END IF; codeGenerator.IL.MarkLabel(end_label, true); if (access == AccessType.None) { codeGenerator.IL.Emit(OpCodes.Pop); return PhpTypeCode.Void; } return PhpTypeCode.Object; }
/// <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; }
private void EmitReturnPhpReference(CodeGenerator codeGenerator) { ILEmitter il = codeGenerator.IL; PhpTypeCode result; if (expr != null) { result = expr.Emit(codeGenerator); if (result != PhpTypeCode.PhpReference) { // return value is "boxed" to PhpReference: if (result != PhpTypeCode.Void) { codeGenerator.EmitBoxing(result); // We can box the value without making a copy since the result of the return expression // is not accessible after returnign from the routine as it is a value (not a reference). il.Emit(OpCodes.Newobj, Constructors.PhpReference_Object); } else { il.Emit(OpCodes.Newobj, Constructors.PhpReference_Void); } } } else { il.Emit(OpCodes.Newobj, Constructors.PhpReference_Void); } codeGenerator.EmitReturnBranch(); }
/// <summary> /// Return value is not deeply copied since the deep copy takes place when the caller accesses the value. /// </summary> private void EmitReturnObject(CodeGenerator/*!*/ codeGenerator) { ILEmitter il = codeGenerator.IL; PhpTypeCode result; if (expr != null) { result = expr.Emit(codeGenerator); // dereference return value: if (result == PhpTypeCode.PhpReference) { il.Emit(OpCodes.Ldfld, Fields.PhpReference_Value); } else if (result == PhpTypeCode.PhpArray) { // <array>.InplaceCopyOnReturn = true; il.Emit(OpCodes.Dup); il.Emit(OpCodes.Ldc_I4_1); il.Emit(OpCodes.Call, Properties.PhpArray_InplaceCopyOnReturn.GetSetMethod()); } else { codeGenerator.EmitBoxing(result); } } else { il.Emit(OpCodes.Ldnull); } codeGenerator.EmitReturnBranch(); }
/// <summary> /// Emits strict equality to empty PHP array. /// </summary> /// <param name="codeGenerator">A code generator.</param> /// <param name="expr">Expression to be compared against.</param> private static void EmitEmptyArrayStrictEquality(CodeGenerator/*!*/codeGenerator, Expression/*!*/expr) { if (IsEmptyArrayEx(expr)) { // array() === array() // LOAD true codeGenerator.IL.LoadBool(true); } else if (expr is Literal) { // array() === NULL|int|double|string|... // LOAD false codeGenerator.IL.LoadBool(false); } else { // array() === <expr> // LOAD <expr> var exprTypeCode = expr.Emit(codeGenerator); // check whether <expr> type can be an array switch (exprTypeCode) { case PhpTypeCode.Boolean: case PhpTypeCode.DObject: case PhpTypeCode.Double: case PhpTypeCode.Integer: case PhpTypeCode.LongInteger: case PhpTypeCode.PhpBytes: case PhpTypeCode.PhpString: case PhpTypeCode.String: // always FALSE codeGenerator.IL.Emit(OpCodes.Pop); codeGenerator.IL.LoadBool(false); break; case PhpTypeCode.PhpArray: // compare (PhpArray)<expr> with array() codeGenerator.IL.Emit(OpCodes.Call, Methods.Operators.StrictEmptyPhpArrayEquality_PhpArray); break; default: // compare <expr> with array() codeGenerator.EmitBoxing(exprTypeCode); codeGenerator.IL.Emit(OpCodes.Call, Methods.Operators.StrictEmptyPhpArrayEquality); break; } } }
internal override PhpTypeCode Emit(CodeGenerator codeGenerator) { Debug.Assert(access == AccessType.Read || access == AccessType.None); Statistics.AST.AddNode("Class.Concat." + expressions.Count); PhpTypeCode result; // // For low numbers call specialized methods switch (expressions.Count) { case 1: result = expressions[0].Emit(codeGenerator); if (result != PhpTypeCode.PhpBytes && result != PhpTypeCode.String) { var lbl = codeGenerator.IL.DefineLabel(); codeGenerator.EmitBoxing(result); codeGenerator.IL.Emit(OpCodes.Dup); codeGenerator.IL.Emit(OpCodes.Isinst,typeof(PhpBytes)); // IF (STACK) codeGenerator.IL.Emit(OpCodes.Brtrue_S,lbl); if (true) { codeGenerator.IL.Emit(OpCodes.Call, Methods.Convert.ObjectToString); } // ELSE codeGenerator.IL.MarkLabel(lbl, true); //END IF result = PhpTypeCode.Object; } break; case 2: result = EmitConcat(codeGenerator, expressions[0], expressions[1]); break; default: codeGenerator.EmitObjectArrayPopulation(expressions); codeGenerator.IL.Emit(OpCodes.Call, Methods.Operators.Concat.ObjectArray); result = PhpTypeCode.Object; // string, PhpBytes break; } switch (access) { case AccessType.Read: // do nothing break; case AccessType.None: // pop result from stack codeGenerator.IL.Emit(OpCodes.Pop); result = PhpTypeCode.Void; break; } return result; }
/// <summary> /// Emits load of an argument of a concatenation. /// </summary> private static PhpTypeCode EmitConcatExpressionLoad(CodeGenerator/*!*/ codeGenerator, Expression/*!*/ expression) { // tries to evaluate the expression: if (expression.HasValue) { if (expression.Value is PhpBytes) { codeGenerator.IL.LoadLiteral(expression.Value); return PhpTypeCode.PhpBytes; } else { // evaluated expression is converted to a string if necessary: codeGenerator.IL.Emit(OpCodes.Ldstr, Convert.ObjectToString(expression.Value)); return PhpTypeCode.String; } } else { // emits non-evaluable expression: PhpTypeCode result = expression.Emit(codeGenerator); // the result should be converted to string: (so we know the type for the further analysis) if (result != PhpTypeCode.String && // string already result != PhpTypeCode.Object && // object can contain PhpBytes, should be converted just when we know we need string result != PhpTypeCode.PhpBytes // keep PhpBytes ) { codeGenerator.EmitBoxing(result); // in case of value-type codeGenerator.IL.Emit(OpCodes.Call, Methods.Convert.ObjectToString); result = PhpTypeCode.String; } return result; } }
/// <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("IssetEx"); ILEmitter il = codeGenerator.IL; codeGenerator.ChainBuilder.Create(); codeGenerator.ChainBuilder.QuietRead = true; if (varList.Count == 1) { codeGenerator.EmitBoxing(varList[0].EmitIsset(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(varList[0].EmitIsset(codeGenerator, false)); // Compare the result with "null" il.CmpNotNull(); // Process following variables and include branching for (int i = 1; i < varList.Count; i++) { il.Emit(OpCodes.Brfalse, f_label); codeGenerator.EmitBoxing(varList[i].EmitIsset(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; }
/// <summary> /// Emits IL instructions that load actual parameters and optionally add a new stack frame to /// current <see cref="PHP.Core.ScriptContext.Stack"/>. /// </summary> /// <param name="node">Instance.</param> /// <param name="codeGenerator">Code generator.</param> /// <remarks> /// Nothing is expected on the evaluation stack. Nothing is left on the evaluation stack. /// </remarks> public void EmitLoadOnPhpStack(CallSignature/*!*/node, CodeGenerator/*!*/ codeGenerator) { List<ActualParam> parameters = node.Parameters; List<TypeRef> genericParams = node.GenericParams; PhpStackBuilder.EmitAddFrame(codeGenerator.IL, codeGenerator.ScriptContextPlace, genericParams.Count, parameters.Count, delegate(ILEmitter il, int i) { // generic arguments: genericParams[i].EmitLoadTypeDesc(codeGenerator, ResolveTypeFlags.UseAutoload | ResolveTypeFlags.ThrowErrors); }, delegate(ILEmitter il, int i) { // regular arguments: var p = parameters[i]; codeGenerator.EmitBoxing(p.NodeCompiler<ActualParamCompiler>().Emit(p, codeGenerator)); } ); }
/// <include file='Doc/Nodes.xml' path='doc/method[@name="Emit"]/*'/> /// <remarks> /// Nothing is expected on the evaluation stack. The result value is left on the /// evaluation stack. /// </remarks> internal override PhpTypeCode Emit(CodeGenerator codeGenerator) { Debug.Assert(access == AccessType.Read || access == AccessType.None); Statistics.AST.AddNode("EmptyEx"); codeGenerator.ChainBuilder.Create(); codeGenerator.ChainBuilder.QuietRead = true; // call EmitIsset in order to evaluate the variable quietly codeGenerator.EmitBoxing(variable.EmitIsset(codeGenerator, true)); codeGenerator.IL.Emit(OpCodes.Call, Methods.PhpVariable.IsEmpty); codeGenerator.ChainBuilder.End(); if (access == AccessType.None) { codeGenerator.IL.Emit(OpCodes.Pop); return PhpTypeCode.Void; } return PhpTypeCode.Boolean; }
/// <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) { // not emitted in release mode: Debug.Assert(kind != EvalKinds.LambdaFunction, "Invalid eval kind."); Debug.Assert(kind != EvalKinds.Assert || codeGenerator.Context.Config.Compiler.Debug, "Assert should be cut off in release mode."); Debug.Assert(access == AccessType.None || access == AccessType.Read || access == AccessType.ReadRef); Debug.Assert(inlinedCode != null || codeGenerator.RTVariablesTablePlace != null, "Function should have variables table."); Statistics.AST.AddNode("EvalEx"); ILEmitter il = codeGenerator.IL; PhpTypeCode result; if (inlinedCode != null) { Debug.Assert(kind == EvalKinds.Assert, "Only assert can be inlined so far."); Label endif_label = il.DefineLabel(); Label else_label = il.DefineLabel(); // IF DynamicCode.PreAssert(context) THEN codeGenerator.EmitLoadScriptContext(); il.Emit(OpCodes.Call, Methods.DynamicCode.PreAssert); il.Emit(OpCodes.Brfalse, else_label); if (true) { // LOAD <evaluated assertion>; codeGenerator.EmitBoxing(((Expression)code).Emit(codeGenerator)); // CALL DynamicCode.PostAssert(context); codeGenerator.EmitLoadScriptContext(); il.Emit(OpCodes.Call, Methods.DynamicCode.PostAssert); // LOAD bool CheckAssertion(STACK, <inlined code>, context, <source path>, line, column); il.Emit(OpCodes.Ldstr, inlinedCode); codeGenerator.EmitLoadScriptContext(); il.Emit(OpCodes.Ldstr, codeGenerator.SourceUnit.SourceFile.RelativePath.ToString()); il.LdcI4(this.position.FirstLine); il.LdcI4(this.position.FirstColumn); codeGenerator.EmitLoadNamingContext(); il.Emit(OpCodes.Call, Methods.DynamicCode.CheckAssertion); // GOTO END IF; il.Emit(OpCodes.Br, endif_label); } // ELSE il.MarkLabel(else_label); if (true) { // LOAD true; il.Emit(OpCodes.Ldc_I4_1); } // END IF; il.MarkLabel(endif_label); result = PhpTypeCode.Object; } else { result = codeGenerator.EmitEval(kind, code, position, currentNamespace, aliases); } // handles return value according to the access type: codeGenerator.EmitReturnValueHandling(this, false, ref result); return result; }
/// <summary> /// Emits call to a default comparator method. /// </summary> /// <param name="codeGenerator">A code generator.</param> /// <param name="equality">Whether to emit equality comparison (or generic comparison otherwise).</param> /// <returns>A type code of the result.</returns> private PhpTypeCode EmitComparison(CodeGenerator codeGenerator, bool equality) { PhpTypeCode x, y; // PhpComparer.Default.<CompareEq | Compare>(box left, box right <|, false>); /*changed to static method*/ //codeGenerator.IL.Emit(OpCodes.Ldsfld, Fields.PhpComparer_Default); if (equality) { return codeGenerator.EmitCompareEq(cg => this.leftExpr.Emit(cg), cg => this.rightExpr.Emit(cg)); } else { x = leftExpr.Emit(codeGenerator); if (x == PhpTypeCode.Integer) { y = rightExpr.Emit(codeGenerator); // int, ? if (y == PhpTypeCode.Integer) { // int, int codeGenerator.IL.Emit(OpCodes.Call, Methods.CompareOp_int_int); return PhpTypeCode.Integer; } else { codeGenerator.EmitBoxing(y); // int, object codeGenerator.IL.LdcI4(0); // throws = false codeGenerator.IL.Emit(OpCodes.Call, Methods.CompareOp_int_object_bool); return PhpTypeCode.Integer; } } else { codeGenerator.EmitBoxing(x); y = rightExpr.Emit(codeGenerator); // object, ? if (y == PhpTypeCode.Integer) { // object, int codeGenerator.IL.LdcI4(0); // throws = false codeGenerator.IL.Emit(OpCodes.Call, Methods.CompareOp_object_int_bool); return PhpTypeCode.Integer; } else { codeGenerator.EmitBoxing(y); // object, object codeGenerator.IL.LdcI4(0); // throws = false codeGenerator.IL.Emit(OpCodes.Call, Methods.CompareOp_object_object_bool); return PhpTypeCode.Integer; } } } }
/// <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("ExitEx"); codeGenerator.EmitLoadScriptContext(); if (resultExpr == null) { codeGenerator.IL.Emit(OpCodes.Ldnull); } else { codeGenerator.EmitBoxing(resultExpr.Emit(codeGenerator)); } codeGenerator.IL.Emit(OpCodes.Call, Methods.ScriptContext.Die); if (access == AccessType.Read) { codeGenerator.IL.Emit(OpCodes.Ldnull); return PhpTypeCode.Object; } else return PhpTypeCode.Void; }
/// <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; }
internal void Emit(CodeGenerator codeGenerator) { ILEmitter il = codeGenerator.IL; string id = codeGenerator.GetLocationId(); if (id == null) { // we are in global code -> just assign the iniVal to the variable variable.Emit(codeGenerator); if (initializer != null) { codeGenerator.EmitBoxing(initializer.Emit(codeGenerator)); il.Emit(OpCodes.Newobj, Constructors.PhpReference_Object); } else il.Emit(OpCodes.Newobj, Constructors.PhpReference_Void); variable.EmitAssign(codeGenerator); } else { // (J): cache the integer index of static local variable to access its value fast from within the array // unique static local variable string ID id = String.Format("{0}${1}${2}${3}", id, variable.VarName, position.FirstLine, position.FirstColumn); // create static field for static local index: private static int <id>; var type = codeGenerator.IL.TypeBuilder; Debug.Assert(type != null, "The method does not have declaring type! (global code in pure mode?)"); var field_id = type.DefineField(id, Types.Int[0], System.Reflection.FieldAttributes.Private | System.Reflection.FieldAttributes.Static); // we are in a function or method -> try to retrieve the local value from ScriptContext variable.Emit(codeGenerator); // <context>.GetStaticLocal( <field> ) codeGenerator.EmitLoadScriptContext(); // <context> il.Emit(OpCodes.Ldsfld, field_id); // <field> il.Emit(OpCodes.Callvirt, Methods.ScriptContext.GetStaticLocal); // GetStaticLocal il.Emit(OpCodes.Dup); // ?? <context>.AddStaticLocal( <field> != 0 ? <field> : ( <field> = ScriptContext.GetStaticLocalId(<id>) ), <initializer> ) if (true) { // if (GetStaticLocal(<field>) == null) Label local_initialized = il.DefineLabel(); il.Emit(OpCodes.Brtrue/*not .S, initializer can emit really long code*/, local_initialized); il.Emit(OpCodes.Pop); // <field> != 0 ? <field> : ( <field> = ScriptContext.GetStaticLocalId(<id>) ) il.Emit(OpCodes.Ldsfld, field_id); // <field> if (true) { // if (<field> == 0) Label id_initialized = il.DefineLabel(); il.Emit(OpCodes.Brtrue_S, id_initialized); // <field> = GetStaticLocalId( <id> ) il.Emit(OpCodes.Ldstr, id); il.Emit(OpCodes.Call, Methods.ScriptContext.GetStaticLocalId); il.Emit(OpCodes.Stsfld, field_id); il.MarkLabel(id_initialized); } // <context>.AddStaticLocal(<field>,<initialize>) codeGenerator.EmitLoadScriptContext(); // <context> il.Emit(OpCodes.Ldsfld, field_id); // <field> if (initializer != null) codeGenerator.EmitBoxing(initializer.Emit(codeGenerator)); // <initializer> else il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Callvirt, Methods.ScriptContext.AddStaticLocal); // AddStaticLocal // il.MarkLabel(local_initialized); } // (J) Following code used Dictionary. It was replaced by the code above. /* codeGenerator.EmitLoadScriptContext(); il.Emit(OpCodes.Ldstr, id); il.Emit(OpCodes.Call, Methods.ScriptContext.GetStaticLocal); Label reference_gotten = codeGenerator.IL.DefineLabel(); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Brtrue, reference_gotten); il.Emit(OpCodes.Pop); // this is the first time execution reach the statement for current request -> initialize the local codeGenerator.EmitLoadScriptContext(); il.Emit(OpCodes.Ldstr, id); if (initializer != null) codeGenerator.EmitBoxing(initializer.Emit(codeGenerator)); else il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Call, Methods.ScriptContext.AddStaticLocal); // assign the resulting PhpReference into the variable il.MarkLabel(reference_gotten, true); */ variable.EmitAssign(codeGenerator); } }
private PhpTypeCode EmitNodeReadUnknown(CodeGenerator codeGenerator) { if (codeGenerator.ChainBuilder.IsMember) { // 1,4,5,6,9 if (this.isMemberOf != null) { // 1: ...->$a->... codeGenerator.ChainBuilder.Lengthen(); // for hop over -> PhpTypeCode res = isMemberOf.Emit(codeGenerator); if (res != PhpTypeCode.PhpRuntimeChain) { codeGenerator.EmitBoxing(res); codeGenerator.ChainBuilder.EmitCreateRTChain(); } codeGenerator.ChainBuilder.EmitRTChainAddField(this); return PhpTypeCode.PhpRuntimeChain; } if (codeGenerator.ChainBuilder.IsArrayItem && !codeGenerator.ChainBuilder.IsLastMember) { // 6: $b->${"a"}[3] codeGenerator.ChainBuilder.EmitRTChainAddField(this); return PhpTypeCode.PhpRuntimeChain; } // 4: ${"a"}[][] // 5: $$a->b->c->... // 9: $$a->b this.EmitLoadRef(codeGenerator); codeGenerator.ChainBuilder.EmitCreateRTChain(); return PhpTypeCode.PhpRuntimeChain; } // 2,3,7,8 if (this.isMemberOf != null) { // 2: $b->$a // 8: b[]->$a codeGenerator.ChainBuilder.Create(); codeGenerator.ChainBuilder.Begin(); codeGenerator.ChainBuilder.Lengthen(); // for hop over -> PhpTypeCode res = isMemberOf.Emit(codeGenerator); if (res != PhpTypeCode.PhpRuntimeChain) { codeGenerator.EmitBoxing(res); codeGenerator.ChainBuilder.EmitCreateRTChain(); } codeGenerator.ChainBuilder.EmitRTChainAddField(this); codeGenerator.ChainBuilder.End(); return PhpTypeCode.PhpRuntimeChain; } // 3: ${"a"}[3] // 7: $$a this.EmitLoadRef(codeGenerator); return PhpTypeCode.PhpReference; }
/// <summary> /// Emits the literal. The common code for all literals. /// </summary> internal override PhpTypeCode Emit(CodeGenerator/*!*/ codeGenerator) { ILEmitter il = codeGenerator.IL; // loads the value: il.LoadLiteral(Value); switch (access) { case AccessType.Read: return ValueTypeCode; case AccessType.None: il.Emit(OpCodes.Pop); return ValueTypeCode; case AccessType.ReadUnknown: case AccessType.ReadRef: // created by evaluation a function called on literal, e.g. $x =& sin(10); codeGenerator.EmitBoxing(ValueTypeCode); il.Emit(OpCodes.Newobj, Constructors.PhpReference_Object); return PhpTypeCode.PhpReference; } Debug.Fail("Invalid access type"); return PhpTypeCode.Invalid; }