internal PhpTypeCode Emit(CodeGenerator /*!*/ codeGenerator, bool ensureChainWritable) { codeGenerator.ChainBuilder.Create(); if (ensureChainWritable) { codeGenerator.ChainBuilder.EnsureWritable = true; } try { return(expression.Emit(codeGenerator)); } finally { codeGenerator.ChainBuilder.End(); } }
/// <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); } }
/// <summary> /// Emits IL instructions that load the value of an item of given array. /// </summary> /// <param name="array"><see cref="Expression"/> determining the array.</param> /// <param name="index"><see cref="Expression"/> determining the index whose value /// should be obtained from the array.</param> /// <param name="kind">A kind of getter.</param> /// <remarks>Nothing is supposed on the evaluation stack. The value of the item is left /// on the evaluation stack.</remarks> public PhpTypeCode EmitGetItem(Expression/*!*/ array, Expression/*!*/ index, Operators.GetItemKinds kind) { ILEmitter il = codeGenerator.IL; // array: var arrayTypeCode = array.Emit(codeGenerator); // ensure the array is writeable is required if (EnsureWritable) codeGenerator.EmitEnsureWritable(arrayTypeCode); // index: PhpTypeCode index_type_code = codeGenerator.EmitArrayKey(this, index); // kind: if (kind == Operators.GetItemKinds.Get && QuietRead) kind = Operators.GetItemKinds.QuietGet; il.LdcI4((int)kind); // CALL Operators.GetItem(<array>, <index>, <kind>) codeGenerator.EmitGetItem(index_type_code, index, false); return PhpTypeCode.Object; }
public void EmitUnsetItem(Expression array, Expression index) { // Template: // void UnsetItem(object var,object index) array.Emit(codeGenerator); Debug.Assert(index != null); Create(); codeGenerator.EmitBoxing(index.Emit(codeGenerator)); End(); codeGenerator.IL.Emit(OpCodes.Call, Methods.Operators.UnsetItem); }
/// <author>Tomas Matousek</author> /// <remarks> /// Emits the following code: /// <code> /// IPhpEnumerable enumerable = ARRAY as IPhpEnumerable; /// if (enumerable==null) /// { /// PhpException.InvalidForeachArgument(); /// } /// else /// FOREACH_BEGIN: /// { /// IDictionaryEnumerator enumerator = enumerable.GetForeachEnumerator(KEYED,ALIASED,TYPE_HANDLE); /// /// goto LOOP_TEST; /// LOOP_BEGIN: /// { /// ASSIGN(value,enumerator.Value); /// ASSIGN(key,enumerator.Key); /// /// BODY; /// } /// LOOP_TEST: /// if (enumerator.MoveNext()) goto LOOP_BEGIN; /// } /// FOREACH_END: /// </code> /// </remarks> /// <include file='Doc/Nodes.xml' path='doc/method[@name="Emit"]/*'/> internal override void Emit(CodeGenerator codeGenerator) { Statistics.AST.AddNode("Loop.Foreach"); ILEmitter il = codeGenerator.IL; Label foreach_end = il.DefineLabel(); Label foreach_begin = il.DefineLabel(); Label loop_begin = il.DefineLabel(); Label loop_test = il.DefineLabel(); codeGenerator.BranchingStack.BeginLoop(loop_test, foreach_end, codeGenerator.ExceptionBlockNestingLevel); LocalBuilder enumerable = il.GetTemporaryLocal(typeof(IPhpEnumerable)); // marks foreach "header" (the first part of the IL code): codeGenerator.MarkSequencePoint( enumeree.Position.FirstLine, enumeree.Position.FirstColumn, valueVariable.Position.LastLine, valueVariable.Position.LastColumn + 1); // enumerable = array as IPhpEnumerable; enumeree.Emit(codeGenerator); il.Emit(OpCodes.Isinst, typeof(IPhpEnumerable)); il.Stloc(enumerable); // if (enumerable==null) il.Ldloc(enumerable); il.Emit(OpCodes.Brtrue, foreach_begin); { // CALL PhpException.InvalidForeachArgument(); codeGenerator.EmitPhpException(Methods.PhpException.InvalidForeachArgument); il.Emit(OpCodes.Br, foreach_end); } // FOREACH_BEGIN: il.MarkLabel(foreach_begin); { LocalBuilder enumerator = il.GetTemporaryLocal(typeof(System.Collections.IDictionaryEnumerator)); // enumerator = enumerable.GetForeachEnumerator(KEYED,ALIASED,TYPE_HANDLE); il.Ldloc(enumerable); il.LoadBool(keyVariable != null); il.LoadBool(valueVariable.Alias); codeGenerator.EmitLoadClassContext(); il.Emit(OpCodes.Callvirt, Methods.IPhpEnumerable_GetForeachEnumerator); il.Stloc(enumerator); // goto LOOP_TEST; il.Emit(OpCodes.Br, loop_test); // LOOP_BEGIN: il.MarkLabel(loop_begin); { // enumerator should do dereferencing and deep copying (if applicable): // ASSIGN(value,enumerator.Value); valueVariable.Emit(codeGenerator); il.Ldloc(enumerator); il.Emit(OpCodes.Callvirt, Properties.IDictionaryEnumerator_Value.GetGetMethod()); if (valueVariable.Alias) { il.Emit(OpCodes.Castclass, typeof(PhpReference)); } valueVariable.EmitAssign(codeGenerator); if (keyVariable != null) { // enumerator should do dereferencing and deep copying (if applicable): // ASSIGN(key,enumerator.Key); keyVariable.Emit(codeGenerator); il.Ldloc(enumerator); il.Emit(OpCodes.Callvirt, Properties.IDictionaryEnumerator_Key.GetGetMethod()); keyVariable.EmitAssign(codeGenerator); } // BODY: body.Emit(codeGenerator); } // LOOP_TEST: il.MarkLabel(loop_test); // marks foreach "header" (the second part of the code): codeGenerator.MarkSequencePoint( enumeree.Position.FirstLine, enumeree.Position.FirstColumn, valueVariable.Position.LastLine, valueVariable.Position.LastColumn + 1); // if (enumerator.MoveNext()) goto LOOP_BEGIN; il.Ldloc(enumerator); il.Emit(OpCodes.Callvirt, Methods.IEnumerator_MoveNext); il.Emit(OpCodes.Brtrue, loop_begin); // il.ReturnTemporaryLocal(enumerator); } // FOREACH_END: il.MarkLabel(foreach_end); il.ReturnTemporaryLocal(enumerable); codeGenerator.BranchingStack.EndLoop(); il.ForgetLabel(foreach_end); il.ForgetLabel(foreach_begin); il.ForgetLabel(loop_begin); il.ForgetLabel(loop_test); }
internal PhpTypeCode EmitArrayKey(ChainBuilder chain, Expression key) { PhpTypeCode result; if (key != null) { if (chain != null) chain.Create(); // convert the key into integer if necessary and possible in compile time IntStringKey array_key; if (key.HasValue() && Convert.ObjectToArrayKey(key.GetValue(), out array_key) && array_key.IsInteger) { il.LdcI4(array_key.Integer); result = PhpTypeCode.Integer; } else { // Emit index and box the result switch (result = key.Emit(this)) { case PhpTypeCode.Integer: break; case PhpTypeCode.String: break; default: EmitBoxing(result); result = PhpTypeCode.Object; break; } } if (chain != null) chain.End(); } else result = PhpTypeCode.Invalid; return result; }
/// <summary> /// Emits IL instructions that convert the top of evaluation stack to a specified type. /// </summary> /// <remarks> /// Emits a call to one of <see cref="PHP.Core.Convert"/> methods to do the conversion. /// The method result is left on the evaluation stack. /// </remarks> internal void EmitConversion(Expression/*!*/ expression, PhpTypeCode dst) { // expression is evaluable: if (expression.HasValue()) { switch (dst) { case PhpTypeCode.String: il.Emit(OpCodes.Ldstr, PHP.Core.Convert.ObjectToString(expression.GetValue())); break; case PhpTypeCode.Boolean: il.LdcI4(PHP.Core.Convert.ObjectToBoolean(expression.GetValue()) ? 1 : 0); break; case PhpTypeCode.Integer: il.LdcI4(PHP.Core.Convert.ObjectToInteger(expression.GetValue())); break; case PhpTypeCode.Double: il.Emit(OpCodes.Ldc_R8, PHP.Core.Convert.ObjectToDouble(expression.GetValue())); break; case PhpTypeCode.Object: il.LoadLiteral(expression.GetValue()); break; default: Debug.Fail("Conversion not implemented."); break; } } else { // emits the expression: PhpTypeCode src = expression.Emit(this); // emits no conversion if types are the same: if (src == dst) return; // emits boxing if needed (conversion methods takes object): EmitBoxing(src); switch (dst) { case PhpTypeCode.String: il.Emit(OpCodes.Call, Methods.Convert.ObjectToString); break; case PhpTypeCode.Boolean: il.Emit(OpCodes.Call, Methods.Convert.ObjectToBoolean); break; case PhpTypeCode.Integer: il.Emit(OpCodes.Call, Methods.Convert.ObjectToBoolean); break; case PhpTypeCode.Double: il.Emit(OpCodes.Call, Methods.Convert.ObjectToDouble); break; case PhpTypeCode.Object: // nop // break; default: Debug.Fail("Conversion is not implemented."); break; } } }
/// <summary> /// Emits IL instructions to process the <B>echo</B> and <B>print</B> commands. /// </summary> /// <param name="parameter">Expression to be sent to output.</param> public void EmitEcho(Expression parameter) { // Template: // context.Echo(value); ConcatEx concat; //BinaryEx binary_expr; if ((concat = parameter as ConcatEx) != null && concat.Expressions.Length > 1) { //foreach (Expression expr in concat.Expressions) //{ // EmitLoadScriptContext(); // EmitEchoCall(expr.Emit(this)); //} // obsolete: (but expressions must be first emitted and processed, then echoed) // array = new object[] { expr1, expr2, ..., exprn }; //LocalBuilder array = EmitObjectArrayPopulation(concat.Expressions, null); //// context.Echo(array); //EmitLoadScriptContext(); //il.Ldloc(array); //il.Emit(OpCodes.Call, Methods.ScriptContext.Echo.ObjectArray); EmitEcho(concat.Expressions); } //// obsolete: wrong order of expressions execution (evaluate first, then echo!) //else if ((binary_expr = parameter as BinaryEx) != null && binary_expr.Operation == Operations.Concat) //{ // // context.Echo(<left>) // EmitLoadScriptContext(); // EmitEchoCall(binary_expr.LeftExpr.Emit(this)); // // context.Echo(<right>) // EmitLoadScriptContext(); // EmitEchoCall(binary_expr.RightExpr.Emit(this)); //} else { var typecode = parameter.Emit(this); EmitLoadScriptContext(); // CALL ScriptContext.Echo(<parameter>, <context>) EmitEchoStaticCall(typecode); } }
/// <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); }
internal PhpTypeCode EmitCaseValue(CodeGenerator codeGenerator) { return(caseVal.Emit(codeGenerator)); }
/// <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); } }
/// <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); }
/// <summary> /// Emits IL instructions that ensure that a static field is of <see cref="PhpObject"/> or <see cref="PhpArray"/> /// type. Handles the case when field name is unknown at compile time (see <see cref="AST.IndirectStFldUse"/>). /// </summary> /// <param name="typeRef">The class name (identifier index).</param> /// <param name="propertyName">The property name.</param> /// <param name="propertyNameExpr">The expression that evaluates to property name.</param> /// <param name="ensureArray">Whether to ensure that static field is an array (or an object).</param> /// <remarks> /// Nothing is expected on the evaluation stack. A <see cref="PhpArray"/> or <see cref="DObject"/> is left on the /// evaluation stack. /// </remarks> public PhpTypeCode EmitEnsureStaticProperty(TypeRef typeRef, VariableName? propertyName, Expression propertyNameExpr, bool ensureArray) { Debug.Assert(propertyName != null ^ propertyNameExpr != null); ResolveTypeFlags flags = ResolveTypeFlags.UseAutoload | ResolveTypeFlags.ThrowErrors; // LOAD Operators.EnsureStaticFieldIs[Object|Array](<type desc>, <field name>, <type desc>, <context>) typeRef.EmitLoadTypeDesc(codeGenerator, flags); if (propertyNameExpr != null) codeGenerator.EmitBoxing(propertyNameExpr.Emit(codeGenerator)); else codeGenerator.IL.Emit(OpCodes.Ldstr, propertyName.Value.ToString()); codeGenerator.EmitLoadClassContext(); codeGenerator.EmitLoadScriptContext(); if (ensureArray) codeGenerator.IL.Emit(OpCodes.Call, Methods.Operators.EnsureStaticPropertyIsArray); else codeGenerator.IL.Emit(OpCodes.Call, Methods.Operators.EnsureStaticPropertyIsObject); EmitErrorCheck(ensureArray); return (ensureArray) ? PhpTypeCode.PhpArray : PhpTypeCode.DObject; }
/// <include file='Doc/Nodes.xml' path='doc/method[@name="Emit"]/*'/> internal override PhpTypeCode Emit(CodeGenerator /*!*/ codeGenerator) { Debug.Assert(access == AccessType.None || access == AccessType.Read || access == AccessType.ReadRef || access == AccessType.ReadUnknown); Statistics.AST.AddNode("Assign.Ref"); //ChainBuilder.RefErrorLabelInfo labelInfo; ILEmitter il = codeGenerator.IL; // 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(); lvalue.Emit(codeGenerator); // LOAD <right hand side>: codeGenerator.ChainBuilder.Create(); 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 il.Emit(OpCodes.Dup); // STORE tmp il.Stloc(il.GetAssignmentLocalRef()); // STORE prepared,result lvalue.EmitAssign(codeGenerator); // LOAD DEREF tmp il.Ldloc(il.GetAssignmentLocalRef()); if (access == AccessType.Read) { il.Emit(OpCodes.Ldfld, Fields.PhpReference_Value); result = PhpTypeCode.Object; } else { result = PhpTypeCode.PhpReference; } break; } case AccessType.None: lvalue.EmitAssign(codeGenerator); result = PhpTypeCode.Void; break; default: Debug.Fail(); result = PhpTypeCode.Invalid; break; } codeGenerator.ChainBuilder.End(); return(result); }
/// <summary> /// Emits IL instructions that ensure that the specified item of specified array is of /// type <see cref="PhpArray"/>. /// </summary> /// <param name="array">Array which item is examined.</param> /// <param name="index">Index determining the item which should be examined (can be <B>null</B>).</param> /// <param name="ensureArray">Whether to ensure that static field is an array (or an object).</param> /// <remarks> /// This method is used in operators chains. Nothing is expected on the evaluation stack. /// If the item is of type <see cref="PhpArray"/> and <see cref="PhpObject"/> (respectively) /// it is left on the evaluation stack. Otherwise the control is transfered to the end of chain. /// </remarks> public PhpTypeCode EmitEnsureItem(Expression/*!*/ array, Expression index, bool ensureArray) { if (!ensureArray) Lengthen(); array.Emit(codeGenerator); if (index != null) { // keyed item: Create(); codeGenerator.EmitBoxing(index.Emit(codeGenerator)); End(); if (ensureArray) { codeGenerator.IL.Emit(OpCodes.Callvirt, Methods.PhpArray.EnsureItemIsArray_Object); } else { codeGenerator.EmitLoadScriptContext(); codeGenerator.IL.Emit(OpCodes.Callvirt, Methods.PhpArray.EnsureItemIsObject_Object); } } else { // key-less item: if (ensureArray) { codeGenerator.IL.Emit(OpCodes.Callvirt, Methods.PhpArray.EnsureItemIsArray); } else { codeGenerator.EmitLoadScriptContext(); codeGenerator.IL.Emit(OpCodes.Callvirt, Methods.PhpArray.EnsureItemIsObject); } } EmitErrorCheck(ensureArray); return (ensureArray) ? PhpTypeCode.PhpArray : PhpTypeCode.DObject; }
/// <summary> /// Emit the target of instance method invocation. /// </summary> /// <param name="cg"></param> /// <param name="targetExpr"></param> private static void EmitMethodTargetExpr(PHP.Core.CodeGenerator/*!*/cg, Expression/*!*/targetExpr) { // start a new operators chain (as the rest of chain is read) cg.ChainBuilder.Create(); cg.ChainBuilder.Begin(); cg.ChainBuilder.Lengthen(); // for hop over -> // prepare for operator invocation cg.EmitBoxing(targetExpr.Emit(cg)); cg.ChainBuilder.End(); }
/// <summary> /// Emits a call to a routine with specified name using an operator. /// </summary> internal PhpTypeCode EmitRoutineOperatorCall(DType type, Expression targetExpr, string routineFullName, string fallbackRoutineFullname, Expression routineNameExpr, CallSignature callSignature, AccessType access) { Debug.Assert(routineFullName != null ^ routineNameExpr != null); MethodInfo operator_method; PhpTypeCode return_type_code; // (J) use call sites to call the method: if (targetExpr != null /*|| type != null*/) { Debug.Assert(fallbackRoutineFullname == null); return this.CallSitesBuilder.EmitMethodCall(this, CallSitesBuilder.AccessToReturnType(access), targetExpr, type, routineFullName, routineNameExpr, callSignature); } else if (targetExpr != null) { Debug.Assert(fallbackRoutineFullname == null); // LOAD Operators.InvokeMethod(<target>, <method name>, <type desc>, <context>); // start a new operators chain (as the rest of chain is read) this.ChainBuilder.Create(); this.ChainBuilder.Begin(); this.ChainBuilder.Lengthen(); // for hop over -> // prepare for operator invocation this.EmitBoxing(targetExpr.Emit(this)); this.ChainBuilder.End(); this.EmitName(routineFullName, routineNameExpr, true); this.EmitLoadClassContext(); this.EmitLoadScriptContext(); if (routineFullName != null) operator_method = Methods.Operators.InvokeMethodStr; else operator_method = Methods.Operators.InvokeMethodObj; return_type_code = PhpTypeCode.PhpReference; } else if (type != null) { Debug.Assert(fallbackRoutineFullname == null); // LOAD Operators.InvokeStaticMethod(<type desc>, <method name>, <self>, <type desc>, context); type.EmitLoadTypeDesc(this, ResolveTypeFlags.UseAutoload | ResolveTypeFlags.ThrowErrors); this.EmitName(routineFullName, routineNameExpr, true); this.EmitLoadSelf(); this.EmitLoadClassContext(); this.EmitLoadScriptContext(); operator_method = Methods.Operators.InvokeStaticMethod; return_type_code = PhpTypeCode.PhpReference; } else { Debug.Assert(routineNameExpr == null || fallbackRoutineFullname == null); // (routineNameExpr != null) => (fallbackRoutineFullName == null) // DRoutineDesc <callHint>; FieldInfo hintField = this.CallSitesBuilder.DefineField( "<callHint>'" + (routineFullName ?? "indirect"), typeof(PHP.Core.Reflection.DRoutineDesc), FieldAttributes.Static | FieldAttributes.Assembly); // LOAD ScriptContext.Call{|Void|Value}(<local variables>, <naming context>, <function name>, ref <hint>, context); this.EmitLoadRTVariablesTable(); this.EmitLoadNamingContext(); this.EmitName(routineFullName, routineNameExpr, true); if (fallbackRoutineFullname != null) il.Emit(OpCodes.Ldstr, fallbackRoutineFullname); else il.Emit(OpCodes.Ldnull); // fallback fcn name il.Emit(OpCodes.Ldsflda, hintField); this.EmitLoadScriptContext(); // (J) only necessary copying, dereferencing or reference making: if (access == AccessType.None) { operator_method = Methods.ScriptContext.CallVoid; return_type_code = PhpTypeCode.Void; } else if (access == AccessType.Read) { operator_method = Methods.ScriptContext.CallValue; return_type_code = PhpTypeCode.Object; } else { operator_method = Methods.ScriptContext.Call; return_type_code = PhpTypeCode.PhpReference; } } // emits load of parameters to the PHP stack: callSignature.EmitLoadOnPhpStack(this); // marks transient sequence point just before the call: this.MarkTransientSequencePoint(); il.Emit(OpCodes.Call, operator_method); // marks transient sequence point just after the call: this.MarkTransientSequencePoint(); return return_type_code; }
/// <summary> /// Create and call <see cref="CallSite"/> for getting property. /// </summary> /// <param name="cg"><see cref="CodeGenerator"/>.</param> /// <param name="wantRef">Wheter <see cref="PhpReference"/> is expected as the result.</param> /// <param name="targetExpr">The expression representing the target (object).</param> /// <param name="targetObjectPlace">The place representing the target (<see cref="DObject"/>) iff <paramref name="targetExpr"/> is not provided.</param> /// <param name="targetPlace">The place representing the target (object) iff <paramref name="targetExpr"/> and <paramref name="targetObjectPlace"/> are not provided.</param> /// <param name="targetType">Type of target iff we are getting property statically.</param> /// <param name="fieldName">The name of the field. Can be null if the name is not known at compile time (indirect).</param> /// <param name="fieldNameExpr">The expression used to get field name in run time (iff <paramref name="fieldName"/> is <c>null</c>.</param> /// <param name="issetSemantics">Wheter we are only checking if the property exists. If true, no warnings are thrown during run time.</param> /// <returns>Type code of the value that is pushed onto the top of the evaluation stack.</returns> public PhpTypeCode EmitGetProperty( PHP.Core.CodeGenerator/*!*/cg, bool wantRef, Expression targetExpr, IPlace targetObjectPlace, IPlace targetPlace, DType targetType, string fieldName, Expression fieldNameExpr, bool issetSemantics) { Debug.Assert(fieldName != null ^ fieldNameExpr != null); Debug.Assert(targetExpr != null || targetObjectPlace != null || targetPlace != null || targetType != null); // bool staticCall = (targetExpr == null && targetObjectPlace == null && targetPlace == null); // we are going to access static property bool fieldNameIsKnown = (fieldName != null); bool classContextIsKnown = (this.classContextPlace != null); // // binder flags: // Type returnType = wantRef ? Types.PhpReference[0] : Types.Object[0]; // // define the call site: // // List<Type> additionalArgs = new List<Type>(); if (!classContextIsKnown) additionalArgs.Add(Types.DTypeDesc[0]); if (!fieldNameIsKnown) additionalArgs.Add(Types.String[0]); var delegateTypeArgs = GetPropertyDelegateTypeArgs( staticCall ? Types.DTypeDesc[0] : ((targetObjectPlace != null) ? Types.DObject[0] : Types.Object[0]), // DTypeDesc of static field's declaring type || DObject if field called on DObject known at compile time || otherwise object additionalArgs.ToArray(), returnType); var delegateType = /*System.Linq.Expressions.Expression.*/delegateBuilder.GetDelegateType(delegateTypeArgs, callSitesCount); // (J) do not create dynamic delegates in dynamic modules, so they can be referenced from non-transient assemblies // var field = DefineCallSite(cg.IL, string.Format("get{0}_{1}", wantRef ? "ref" : string.Empty, fieldName ?? "$"), delegateType, (il) => { // <LOAD> Binder.{GetProperty|GetStaticProperty}( fieldName, classContext, issetSemantics, <returnType> ) if (fieldName != null) il.Emit(OpCodes.Ldstr, fieldName); else il.Emit(OpCodes.Ldnull); if (this.classContextPlace != null) this.classContextPlace.EmitLoad(il); else il.Emit(OpCodes.Ldsfld, Fields.UnknownTypeDesc.Singleton); il.LoadBool(issetSemantics); il.Emit(OpCodes.Ldtoken, returnType); il.Emit(OpCodes.Call, Methods.GetTypeFromHandle); il.Emit(OpCodes.Call, staticCall ? Methods.Binder.StaticGetProperty : Methods.Binder.GetProperty); }); // // call the CallSite: // // <field>.Target( <field>, <targetExpr|targetType>, (classContext)?, <methodNameExpr>? ): cg.IL.Emit(OpCodes.Ldsfld, field); cg.IL.Emit(OpCodes.Ldfld, field.FieldType.GetField("Target")); cg.IL.Emit(OpCodes.Ldsfld, field); if (staticCall) targetType.EmitLoadTypeDesc(cg, ResolveTypeFlags.UseAutoload | ResolveTypeFlags.ThrowErrors); else if (targetExpr != null) { cg.ChainBuilder.Lengthen(); // for hop over -> cg.EmitBoxing(targetExpr.Emit(cg)); // prepare for operator invocation } else if (targetObjectPlace != null) targetObjectPlace.EmitLoad(cg.IL); else if (targetPlace != null) targetPlace.EmitLoad(cg.IL); else Debug.Fail(); if (!classContextIsKnown) cg.EmitLoadClassContext(); if (!fieldNameIsKnown) cg.EmitName(fieldName/*null*/, fieldNameExpr, true, PhpTypeCode.String); cg.MarkTransientSequencePoint(); cg.IL.Emit(OpCodes.Callvirt, delegateType.GetMethod("Invoke")); cg.MarkTransientSequencePoint(); // return PhpTypeCodeEnum.FromType(returnType); }
/// <summary> /// Emit call to <see cref="DynamicCode.Assert"/> or <see cref="DynamicCode.Eval"/>. /// </summary> internal PhpTypeCode EmitEval(EvalKinds kind, Expression/*!*/code, Text.Span span, QualifiedName? currentNamespace, Dictionary<string, QualifiedName> currentAliases) { Debug.Assert(code != null); // LOAD DynamicCode.<Eval | Assert>(<code>, context, definedVariables, self, includer, source, line, column, evalId, naming) if (kind == EvalKinds.Assert) { // an argument of the assert is boxed: EmitBoxing(code.Emit(this)); } else if (kind == EvalKinds.SyntheticEval) { Debug.Assert(code.HasValue()); Debug.Assert(code.GetValue() is string); // an argument of the eval is converted to a string: il.Emit(OpCodes.Ldstr, (string)code.GetValue()); il.Emit(OpCodes.Ldc_I4_1); } else { // an argument of the eval is converted to a string: EmitConversion(code, PhpTypeCode.String); il.Emit(OpCodes.Ldc_I4_0); } var position = new Text.TextPoint(this.SourceUnit.LineBreaks, span.Start); EmitLoadScriptContext(); EmitLoadRTVariablesTable(); EmitLoadSelf(); EmitLoadClassContext(); EmitEvalInfoPass(position.Line, position.Column); EmitNamingContext(currentNamespace, currentAliases, span.Start); il.Emit(OpCodes.Call, (kind == EvalKinds.Assert) ? Methods.DynamicCode.Assert : Methods.DynamicCode.Eval); return (kind == EvalKinds.Assert) ? PhpTypeCode.Boolean : PhpTypeCode.Object; }
/// <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; } }
/// <summary> /// Emits conversion to boolean. /// </summary> /// <param name="expr">Expression to be converted.</param> /// <param name="negation">Whether the result should be logic negation of original conversion.</param> internal void EmitObjectToBoolean(Expression/*!*/expr, bool negation) { // <expr> var typecode = expr.Emit(this); // switch (typecode) { case PhpTypeCode.Boolean: if (negation) this.EmitLogicNegation(); break; case PhpTypeCode.Integer: // <int> != 0 this.EmitLogicNegation(); if (!negation) this.EmitLogicNegation(); break; case PhpTypeCode.LongInteger: // <long> != 0 il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Conv_I8); il.Emit(OpCodes.Ceq); if (!negation) this.EmitLogicNegation(); break; case PhpTypeCode.Double: // <double> != 0.0 il.Emit(OpCodes.Ldc_R8, 0.0); il.Emit(OpCodes.Ceq); if (!negation) this.EmitLogicNegation(); break; case PhpTypeCode.Void: if (negation) il.Emit(OpCodes.Ldc_I4_1); else il.Emit(OpCodes.Ldc_I4_0); break; case PhpTypeCode.String: // StringToBoolean( <string> ) IL.Emit(OpCodes.Call, Methods.Convert.StringToBoolean); if (negation) this.EmitLogicNegation(); break; default: // ObjectToBoolean( (object)<expr> ) EmitBoxing(typecode); IL.Emit(OpCodes.Call, Methods.Convert.ObjectToBoolean); if (negation) this.EmitLogicNegation(); break; } }
public void EmitGetArrayItemRef(Expression/*!*/ array, Expression index) { array.Emit(codeGenerator); PhpTypeCode index_type_code = PhpTypeCode.Invalid; if (index != null) index_type_code = codeGenerator.EmitArrayKey(this, index); codeGenerator.EmitGetArrayItem(index_type_code, index, true); }
public void EmitLoad(ILEmitter /*!*/ il) { Debug.Assert(ReferenceEquals(il, codeGenerator.IL)); typeCode = expression.Emit(codeGenerator); }
/// <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; } } }