public override PhpTypeCode Emit(IssetEx node, CodeGenerator codeGenerator) { Debug.Assert(access == AccessType.None || access == AccessType.Read); Statistics.AST.AddNode("IssetEx"); ILEmitter il = codeGenerator.IL; codeGenerator.ChainBuilder.Create(); codeGenerator.ChainBuilder.QuietRead = true; var vars = node.VarList; if (vars.Count == 1) { codeGenerator.EmitBoxing(VariableUseHelper.EmitIsset(vars[0], codeGenerator, false)); // Compare the result with "null" il.CmpNotNull(); } else { // Define labels Label f_label = il.DefineLabel(); Label x_label = il.DefineLabel(); // Get first variable codeGenerator.EmitBoxing(VariableUseHelper.EmitIsset(vars[0], codeGenerator, false)); // Compare the result with "null" il.CmpNotNull(); // Process following variables and include branching for (int i = 1; i < vars.Count; i++) { il.Emit(OpCodes.Brfalse, f_label); codeGenerator.EmitBoxing(VariableUseHelper.EmitIsset(vars[i], codeGenerator, false)); // Compare the result with "null" codeGenerator.IL.CmpNotNull(); } il.Emit(OpCodes.Br, x_label); il.MarkLabel(f_label, true); il.Emit(OpCodes.Ldc_I4_0); il.MarkLabel(x_label, true); } codeGenerator.ChainBuilder.End(); if (access == AccessType.None) { il.Emit(OpCodes.Pop); return(PhpTypeCode.Void); } return(PhpTypeCode.Boolean); }
internal override void Emit(IfStmt node, CodeGenerator codeGenerator) { Statistics.AST.AddNode("IfStmt"); var /*!*/ conditions = node.Conditions; Debug.Assert(conditions.Count > 0); // marks a sequence point containing whole condition: codeGenerator.MarkSequencePoint(conditions[0].Condition); // NOTE: (J) when emitting a statement, sequence point has to be marked. Normally it is done in Statement.Emit() ILEmitter il = codeGenerator.IL; Label exit_label = il.DefineLabel(); Label false_label = il.DefineLabel(); // IF codeGenerator.EmitConversion(conditions[0].Condition, PhpTypeCode.Boolean); il.Emit(OpCodes.Brfalse, false_label); conditions[0].Statement.Emit(codeGenerator); // (J) Mark the end of condition body so debugger will jump off the block properly codeGenerator.MarkSequencePoint(conditions[0].Statement.Span.End); il.Emit(OpCodes.Br, exit_label); // ELSEIF: for (int i = 1; i < conditions.Count && conditions[i].Condition != null; i++) { il.MarkLabel(false_label, true); false_label = il.DefineLabel(); // IF (!<(bool) condition>) codeGenerator.MarkSequencePoint(conditions[i].Condition); // marks a sequence point of the condition "statement" codeGenerator.EmitConversion(conditions[i].Condition, PhpTypeCode.Boolean); il.Emit(OpCodes.Brfalse, false_label); conditions[i].Statement.Emit(codeGenerator); il.Emit(OpCodes.Br, exit_label); } il.MarkLabel(false_label, true); // ELSE if (conditions[conditions.Count - 1].Condition == null) { conditions[conditions.Count - 1].Statement.Emit(codeGenerator); } il.MarkLabel(exit_label, true); }
/// <summary> /// Emits IL instructions that read the value of a field of $this instance. /// </summary> /// <param name="codeGenerator">The current <see cref="CodeGenerator"/>.</param> /// <param name="wantRef">If <B>false</B> the field value should be left on the evaluation stack, /// if <B>true</B> the <see cref="PhpReference"/> should be left on the evaluation stack.</param> /// <returns></returns> private PhpTypeCode EmitReadFieldOfThis(CodeGenerator /*!*/ codeGenerator, bool wantRef) { ILEmitter il = codeGenerator.IL; // $this->a switch (codeGenerator.LocationStack.LocationType) { case LocationTypes.GlobalCode: { // load $this from one of Main's arguments and check for null Label this_non_null = il.DefineLabel(); Label reading_over = il.DefineLabel(); codeGenerator.EmitLoadSelf(); il.Emit(OpCodes.Brtrue_S, this_non_null); EmitThisUsedOutOfObjectThrow(codeGenerator, wantRef); il.Emit(OpCodes.Br, reading_over); il.MarkLabel(this_non_null, true); // call GetObjectProperty/GetObjectPropertyRef EmitGetFieldOfPlace(codeGenerator.SelfPlace, codeGenerator, wantRef); il.MarkLabel(reading_over, true); break; } case LocationTypes.FunctionDecl: { EmitThisUsedOutOfObjectThrow(codeGenerator, wantRef); break; } case LocationTypes.MethodDecl: { CompilerLocationStack.MethodDeclContext context = codeGenerator.LocationStack.PeekMethodDecl(); if (context.Method.IsStatic) { EmitThisUsedOutOfObjectThrow(codeGenerator, wantRef); break; } // attempt direct field reading (DirectVarUse only) return(EmitReadFieldOfThisInInstanceMethod(codeGenerator, wantRef)); } } return(wantRef ? PhpTypeCode.PhpReference : PhpTypeCode.Object); }
/// <summary> /// Emits IL instructions that load the "$this" variable onto the evaluation stack. /// </summary> private PhpTypeCode EmitLoadThis(CodeGenerator codeGenerator) { ILEmitter il = codeGenerator.IL; CompilerLocationStack locationStack = codeGenerator.LocationStack; // special treatment of $this switch (locationStack.LocationType) { case LocationTypes.GlobalCode: { // load $this from one of Main's arguments and check for null Label this_non_null = il.DefineLabel(); codeGenerator.EmitLoadSelf(); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Brtrue_S, this_non_null); il.Emit(OpCodes.Ldstr, VariableName.ThisVariableName.Value); codeGenerator.EmitPhpException(Methods.PhpException.UndefinedVariable); il.MarkLabel(this_non_null, true); return(PhpTypeCode.Object); } case LocationTypes.FunctionDecl: { // always null il.Emit(OpCodes.Ldstr, VariableName.ThisVariableName.Value); codeGenerator.EmitPhpException(Methods.PhpException.UndefinedVariable); il.Emit(OpCodes.Ldnull); return(PhpTypeCode.Object); } case LocationTypes.MethodDecl: { CompilerLocationStack.MethodDeclContext context = locationStack.PeekMethodDecl(); if (context.Method.IsStatic) { // always null in static methods il.Emit(OpCodes.Ldstr, VariableName.ThisVariableName.Value); codeGenerator.EmitPhpException(Methods.PhpException.UndefinedVariable); il.Emit(OpCodes.Ldnull); return(PhpTypeCode.Object); } else { // arg0 or <proxy> in instance methods codeGenerator.EmitLoadSelf(); return(PhpTypeCode.DObject); } } default: Debug.Fail("Invalid location type."); return(PhpTypeCode.Invalid); } }
/// <include file='Doc/Nodes.xml' path='doc/method[@name="Emit"]/*'/> internal override void Emit(CodeGenerator codeGenerator) { Statistics.AST.AddNode("Loop.While"); ILEmitter il = codeGenerator.IL; Label cond_label = il.DefineLabel(); Label exit_label = il.DefineLabel(); Label stat_label = il.DefineLabel(); codeGenerator.BranchingStack.BeginLoop(cond_label, exit_label, codeGenerator.ExceptionBlockNestingLevel); if (this.type == Type.While) { il.Emit(OpCodes.Br, cond_label); } // body: il.MarkLabel(stat_label); body.Emit(codeGenerator); // marks a sequence point containing condition: codeGenerator.MarkSequencePoint( condExpr.Position.FirstLine, condExpr.Position.FirstColumn, condExpr.Position.LastLine, condExpr.Position.LastColumn + 1); // condition: il.MarkLabel(cond_label); // bounded loop: if (condExpr != null) { // IF (<(bool) condition>) GOTO stat; codeGenerator.EmitConversion(condExpr, PhpTypeCode.Boolean); il.Emit(OpCodes.Brtrue, stat_label); } il.MarkLabel(exit_label); codeGenerator.BranchingStack.EndLoop(); il.ForgetLabel(cond_label); il.ForgetLabel(exit_label); il.ForgetLabel(stat_label); }
/// <summary> /// Emits init field helpers (<c>__lastContext</c> field, <c><InitializeInstanceFields></c> /// method and <c>__InitializeStaticFields</c> into a class. /// </summary> internal static void EmitInitFieldHelpers(PhpType phpType) { // // <InitializeInstanceFields> // // <InitializeInstanceFields> method - will contain instance field initialization phpType.Builder.InstanceFieldInit = phpType.RealTypeBuilder.DefineMethod( InstanceFieldInitMethodName, #if SILVERLIGHT MethodAttributes.Public | MethodAttributes.HideBySig, #else MethodAttributes.Private | MethodAttributes.HideBySig, #endif CallingConventions.Standard, Types.Void, Types.ScriptContext); phpType.Builder.InstanceFieldInitEmitter = new ILEmitter(phpType.Builder.InstanceFieldInit); // // <InitializeStaticFields> // // <InitializeStaticFields> method has already been defined during the analysis phase - will contain (thread) // static field initialization ILEmitter cil = new ILEmitter(phpType.StaticFieldInitMethodBuilder); if (phpType.Builder.HasThreadStaticFields) { // __lastContext thread-static field - will contain the last SC that inited static fields for this thread FieldBuilder last_context = phpType.RealTypeBuilder.DefineField( "<lastScriptContext>", Types.ScriptContext[0], FieldAttributes.Private | FieldAttributes.Static); // SILVERLIGHT: Not sure what this does & what would be the right behavior... #if !SILVERLIGHT last_context.SetCustomAttribute(AttributeBuilders.ThreadStatic); #endif // Label init_needed_label = cil.DefineLabel(); // [ if (arg0 == __lastContext) ret ] cil.Emit(OpCodes.Ldarg_0); cil.Emit(OpCodes.Ldsfld, last_context); cil.Emit(OpCodes.Bne_Un_S, init_needed_label); cil.Emit(OpCodes.Ret); // [ __lastContext = arg0 ] cil.MarkLabel(init_needed_label); cil.Emit(OpCodes.Ldarg_0); cil.Emit(OpCodes.Stsfld, last_context); // the rest of the method is created when fields are emitted } }
/// <summary> /// Emits binary boolean operation (AND or OR). /// </summary> /// <param name="codeGenerator">A code generator.</param> /// <param name="isAnd">Whether to emit AND.</param> /// <returns>A type code of the result.</returns> private PhpTypeCode EmitBinaryBooleanOperation(CodeGenerator codeGenerator, bool isAnd) { ILEmitter il = codeGenerator.IL; Label partial_eval_label = il.DefineLabel(); Label end_label = il.DefineLabel(); // IF [!]<(bool) leftSon> THEN GOTO partial_eval; codeGenerator.EmitConversion(leftExpr, PhpTypeCode.Boolean); il.Emit(isAnd ? OpCodes.Brfalse : OpCodes.Brtrue, partial_eval_label); // LOAD <(bool) leftSon>; codeGenerator.EmitConversion(rightExpr, PhpTypeCode.Boolean); il.Emit(OpCodes.Br, end_label); il.MarkLabel(partial_eval_label, true); il.LdcI4(isAnd ? 0 : 1); il.MarkLabel(end_label, true); return(PhpTypeCode.Boolean); }
/// <summary> /// Emits IL instructions that transfer the control to the target label for <B>break</B> statement /// having parameter that cannot be evaluated at compile time. /// </summary> /// <remarks>This function is used to generate code for <B>break v;</B> where <i>v</i> is a variable.</remarks> public void EmitBreakRuntime() { int i; ILEmitter il = codeGenerator.IL; Label[] jumpTable = new Label[stack.Count + 1]; Label exitLabel = il.DefineLabel(); Debug.Assert(stack.Count != 0); for (i = 0; i <= stack.Count; i++) { jumpTable[i] = il.DefineLabel(); } // The value according to we switch is already present on the evaluation stack LocalBuilder break_level_count = il.DeclareLocal(typeof(Int32)); il.Emit(OpCodes.Dup); il.Stloc(break_level_count); il.Emit(OpCodes.Switch, jumpTable); // Default case il.Ldloc(break_level_count); codeGenerator.EmitPhpException(Methods.PhpException.InvalidBreakLevelCount); il.Emit(OpCodes.Br, exitLabel); il.MarkLabel(jumpTable[0]); EmitBranchToExit((StackItem)stack[stack.Count - 1]); for (i = 1; i <= stack.Count; i++) { il.MarkLabel(jumpTable[i]); EmitBranchToExit((StackItem)stack[stack.Count - i]); } il.MarkLabel(exitLabel); }
/// <summary> /// Emits IL instructions that prepare a field of $this for writing. /// </summary> private AssignmentCallback EmitWriteFieldOfThis(CodeGenerator /*!*/ codeGenerator, bool writeRef) { ILEmitter il = codeGenerator.IL; // $this->a switch (codeGenerator.LocationStack.LocationType) { case LocationTypes.GlobalCode: { // load $this from one of Main's arguments and check for null Label this_non_null = il.DefineLabel(); codeGenerator.EmitLoadSelf(); il.Emit(OpCodes.Brtrue_S, this_non_null); codeGenerator.EmitPhpException(Methods.PhpException.ThisUsedOutOfObjectContext); il.Emit(OpCodes.Br, codeGenerator.ChainBuilder.ErrorLabel); il.MarkLabel(this_non_null, true); // prepare the stack for SetObjectProperty call codeGenerator.EmitLoadSelf(); EmitName(codeGenerator); return(new AssignmentCallback(EmitCallSetObjectField)); } case LocationTypes.FunctionDecl: { // always throws error codeGenerator.EmitPhpException(Methods.PhpException.ThisUsedOutOfObjectContext); return(new AssignmentCallback(EmitPopValue)); } case LocationTypes.MethodDecl: { CompilerLocationStack.MethodDeclContext context = codeGenerator.LocationStack.PeekMethodDecl(); if (context.Method.IsStatic) { // always throws error codeGenerator.EmitPhpException(Methods.PhpException.ThisUsedOutOfObjectContext); return(new AssignmentCallback(EmitPopValue)); } // attempt direct field writing (DirectVarUse only) return(EmitWriteFieldOfThisInInstanceMethod(codeGenerator, writeRef)); } } Debug.Fail("Invalid lcoation type."); return(null); }
/// <summary> /// Emits type hint test on the argument if specified. /// </summary> internal void EmitTypeHintTest(CodeGenerator /*!*/ codeGenerator) { int real_index = routine.FirstPhpParameterIndex + index; // not type hint specified: if (typeHint == null) { return; } Debug.Assert(resolvedTypeHint != null); ILEmitter il = codeGenerator.IL; Label endif_label = il.DefineLabel(); // IF (DEREF(ARG[argIdx]) is not of hint type) THEN il.Ldarg(real_index); if (PassedByRef) { il.Emit(OpCodes.Ldfld, Fields.PhpReference_Value); } resolvedTypeHint.EmitInstanceOf(codeGenerator, null); il.Emit(OpCodes.Brtrue, endif_label); // add a branch allowing null values if the argument is optional with null default value (since PHP 5.1.0); if (initValue != null && initValue.HasValue && initValue.Value == null) { // IF (DEREF(ARG[argIdx]) != null) THEN il.Ldarg(real_index); if (PassedByRef) { il.Emit(OpCodes.Ldfld, Fields.PhpReference_Value); } il.Emit(OpCodes.Brfalse, endif_label); } // CALL PhpException.InvalidArgumentType(<param_name>, <class_name>); il.Emit(OpCodes.Ldstr, name.ToString()); il.Emit(OpCodes.Ldstr, resolvedTypeHint.FullName); codeGenerator.EmitPhpException(Methods.PhpException.InvalidArgumentType); // END IF; // END IF; il.MarkLabel(endif_label); }
/// <summary> /// Define new instance of CallSite<<paramref name="delegateType"/>> and initialize it with specified binder. /// </summary> /// <param name="bodyEmitter"><see cref="ILEmitter"/> of the body that is using this call site. This method may emit initialization of the call site into this <paramref name="bodyEmitter"/>.</param> /// <param name="userFriendlyName">User friendly name used as name for the CallSite field.</param> /// <param name="delegateType">CallSite type argument.</param> /// <param name="binderInstanceEmitter">Function used to emit initialization of the binder from within the call sites container .cctor.</param> /// <returns>The <see cref="FieldInfo"/> containing the instance of the created CallSite.</returns> public FieldInfo /*!*/ DefineCallSite(ILEmitter /*!*/ bodyEmitter, string /*!*/ userFriendlyName, Type /*!*/ delegateType, Action <ILEmitter> /*!*/ binderInstanceEmitter) { Debug.Assert(userFriendlyName != null && delegateType != null && binderInstanceEmitter != null); userFriendlyName += ("'" + (callSitesCount++)); // ensures call sites container is initialized // this ensures, {staticCtorEmitter} is set properly var typebuilder = EnsureContainer(); // call site type var callSiteType = Types.CallSiteGeneric[0].MakeGenericType(delegateType); // define the field: // public static readonly CallSite<delegateType> <userFriendlyName> var attrs = FieldAttributes.Static | FieldAttributes.InitOnly | ((staticCtorEmitter == null) ? FieldAttributes.Private : FieldAttributes.Assembly); var field = this.DefineField(userFriendlyName, callSiteType, attrs); if (staticCtorEmitter == null) // => this.classContext != null { // emit initialization of the call site just in the body of current method (as it is in C#, we need current generic arguments): Debug.Assert(this.classContext != null); // check if the call site if not null, otherwise initialize it first: // if (<field> == null) <InitializeCallSite>; Label ifend = bodyEmitter.DefineLabel(); bodyEmitter.Emit(OpCodes.Ldsfld, field); bodyEmitter.Emit(OpCodes.Brtrue, ifend); // init the field: InitializeCallSite(bodyEmitter, callSiteType, field, binderInstanceEmitter); bodyEmitter.MarkLabel(ifend); } else { // init the field in .cctor: InitializeCallSite(staticCtorEmitter, callSiteType, field, binderInstanceEmitter); } // return(field); }
public override PhpTypeCode Emit(AssertEx node, CodeGenerator codeGenerator) { // not emitted in release mode: Debug.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("AssertEx"); ILEmitter il = codeGenerator.IL; PhpTypeCode result; if (_inlinedCode != null) { 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)node.CodeEx).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); var position = new Text.TextPoint(codeGenerator.SourceUnit.LineBreaks, node.Span.Start); il.Emit(OpCodes.Ldstr, _inlinedCode); codeGenerator.EmitLoadScriptContext(); il.Emit(OpCodes.Ldstr, codeGenerator.SourceUnit.SourceFile.RelativePath.ToString()); il.LdcI4(position.Line); il.LdcI4(position.Column); 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(EvalKinds.Assert, node.CodeEx, node.Span, null, null); } // handles return value according to the access type: codeGenerator.EmitReturnValueHandling(node, false, ref result); return(result); }
/// <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 (ScriptContext, bool) constructor. /// </summary> private static void EmitShortConstructor(PhpType /*!*/ phpType) { // (ScriptContext,bool) constructor ConstructorBuilder ctor_builder = phpType.ShortConstructorBuilder; ctor_builder.DefineParameter(1, ParameterAttributes.None, "context"); ctor_builder.DefineParameter(2, ParameterAttributes.None, "newInstance"); ILEmitter cil = new ILEmitter(ctor_builder); // invoke base constructor if (phpType.Base == null) { EmitInvokePhpObjectConstructor(cil); } else { phpType.Base.EmitInvokeConstructor(cil, phpType, null); } // perform fast DObject.<typeDesc> init if we are in its subclass if (phpType.Root is PhpType) { // [ if (GetType() == typeof(self)) this.typeDesc = self.<typeDesc> ] cil.Ldarg(FunctionBuilder.ArgThis); cil.Emit(OpCodes.Call, Methods.Object_GetType); cil.Emit(OpCodes.Ldtoken, phpType.RealTypeBuilder); cil.Emit(OpCodes.Call, Methods.GetTypeFromHandle); Label label = cil.DefineLabel(); cil.Emit(OpCodes.Bne_Un_S, label); if (true) { cil.Ldarg(FunctionBuilder.ArgThis); cil.Emit(OpCodes.Ldsfld, phpType.TypeDescFieldInfo); cil.Emit(OpCodes.Stfld, Fields.DObject_TypeDesc); cil.MarkLabel(label); } } // register this instance for finalization if it introduced the __destruct method DRoutine destruct; if (phpType.TypeDesc.GetMethod(DObject.SpecialMethodNames.Destruct) != null && (phpType.Base == null || phpType.Base.GetMethod(DObject.SpecialMethodNames.Destruct, phpType, out destruct) == GetMemberResult.NotFound)) { cil.Ldarg(FunctionBuilder.ArgContextInstance); cil.Ldarg(FunctionBuilder.ArgThis); if (phpType.ProxyFieldInfo != null) { cil.Emit(OpCodes.Ldfld, phpType.ProxyFieldInfo); } cil.Emit(OpCodes.Call, Methods.ScriptContext.RegisterDObjectForFinalization); } // [ <InitializeInstanceFields>(arg1) ] cil.Ldarg(FunctionBuilder.ArgThis); cil.Ldarg(FunctionBuilder.ArgContextInstance); cil.EmitCall(OpCodes.Call, phpType.Builder.InstanceFieldInit, null); // [ __InitializeStaticFields(arg1) ] cil.Ldarg(FunctionBuilder.ArgContextInstance); cil.EmitCall(OpCodes.Call, phpType.StaticFieldInitMethodInfo, null); cil.Emit(OpCodes.Ret); }
/// <summary> /// Emits the try block and the catch blocks. /// </summary> /// <param name="codeGenerator">A code generator.</param> /// <remarks> /// <code> /// try /// { /// // guarded code // /// } /// catch(E1 $e1) /// { /// // E1 // /// } /// catch(E2 $e2) /// { /// // E2 // /// } /// </code> /// is translated as follows: /// <code> /// try /// { /// // guarded code // /// } /// catch(PhpUserException _e) /// { /// PhpObject _o = _e.UserException; /// if (_o instanceOf E1) /// { /// $e1 = _o; /// // E1 // /// } /// else if (_o instanceOf E2) /// { /// $e2 = _o; /// // E2 // /// } /// else /// { /// throw; /// } /// } /// </code> /// </remarks> internal override void Emit(CodeGenerator /*!*/ codeGenerator) { Statistics.AST.AddNode("TryStmt"); // emit try block without CLR exception block if possible if (!HasCatches && !HasFinallyStatements) { this.Statements.Emit(codeGenerator); return; } // emit CLR exception block ILEmitter il = codeGenerator.IL; codeGenerator.ExceptionBlockNestingLevel++; // TRY Label end_label = il.BeginExceptionBlock(); this.Statements.Emit(codeGenerator); // catches if (HasCatches) { // catch (PHP.Core.ScriptDiedException) // { throw; } il.BeginCatchBlock(typeof(PHP.Core.ScriptDiedException)); il.Emit(OpCodes.Rethrow); // catch (System.Exception ex) il.BeginCatchBlock(typeof(System.Exception)); // <exception_local> = (DObject) (STACK is PhpUserException) ? ((PhpUserException)STACK).UserException : ClrObject.WrapRealObject(STACK) Label clrExceptionLabel = il.DefineLabel(); Label wrapEndLabel = il.DefineLabel(); LocalBuilder exception_local = il.GetTemporaryLocal(typeof(DObject)); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Isinst, typeof(PHP.Core.PhpUserException)); // <STACK> as PhpUserException il.Emit(OpCodes.Brfalse, clrExceptionLabel); // if (<STACK> as PhpUserException != null) { il.Emit(OpCodes.Ldfld, Fields.PhpUserException_UserException); il.Emit(OpCodes.Br, wrapEndLabel); } // else il.MarkLabel(clrExceptionLabel); { il.Emit(OpCodes.Call, Methods.ClrObject_WrapRealObject); } il.MarkLabel(wrapEndLabel); il.Stloc(exception_local); // emits all PHP catch-blocks processing into a single CLI catch-block: foreach (CatchItem c in catches) { Label next_catch_label = il.DefineLabel(); // IF (exception <instanceOf> <type>); c.Emit(codeGenerator, exception_local, end_label, next_catch_label); // ELSE il.MarkLabel(next_catch_label); } il.ReturnTemporaryLocal(exception_local); // emits the "else" branch invoked if the exceptions is not catched: il.Emit(OpCodes.Rethrow); } // finally if (HasFinallyStatements) { finallyItem.Emit(codeGenerator); } // il.EndExceptionBlock(); codeGenerator.ExceptionBlockNestingLevel--; }
public void Emit(StaticVarDecl /*!*/ node, 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 node.Variable.Emit(codeGenerator); if (node.Initializer != null) { codeGenerator.EmitBoxing(node.Initializer.Emit(codeGenerator)); il.Emit(OpCodes.Newobj, Constructors.PhpReference_Object); } else { il.Emit(OpCodes.Newobj, Constructors.PhpReference_Void); } // continue ... } else { // 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}", id, node.Variable.VarName, node.Span.Start); // 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 node.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 (node.Initializer != null) { codeGenerator.EmitBoxing(node.Initializer.Emit(codeGenerator)); // <initializer> } else { il.Emit(OpCodes.Ldnull); } il.Emit(OpCodes.Callvirt, Methods.ScriptContext.AddStaticLocal); // AddStaticLocal // il.MarkLabel(local_initialized); } // continue ... } // stores value from top of the stack to the variable: SimpleVarUseHelper.EmitAssign(node.Variable, codeGenerator); }
/// <summary> /// Emits load of an array where all optional arguments are stored. /// Each optional argument is peeked from the PHP stack and converted before stored to the array. /// The resulting array is pushed on evaluation stack so it can be later passed as an argument to a method. /// </summary> /// <param name="builder">The builder.</param> /// <param name="start">The index of the first argument to be loaded.</param> /// <param name="param">The last parameter of the overload (should be an array).</param> /// <param name="optArgCount">The place where the number of optional arguments is stored.</param> /// <remarks>Assumes that the non-negative number of optional arguments has been stored to /// <paramref name="optArgCount"/> place.</remarks> public static void EmitPeekAllArguments(OverloadsBuilder /*!*/ builder, int start, ParameterInfo param, IPlace optArgCount) { Debug.Assert(start >= 0 && optArgCount != null && param != null); ILEmitter il = builder.IL; Type elem_type = param.ParameterType.GetElementType(); Type array_type = Type.GetType(elem_type.FullName + "[]", true); Type actual_type; // declares aux. variables: LocalBuilder loc_array = il.DeclareLocal(array_type); LocalBuilder loc_i = il.DeclareLocal(typeof(int)); LocalBuilder loc_elem = il.DeclareLocal(elem_type); // creates an array for the arguments // array = new <elem_type>[opt_arg_count]: optArgCount.EmitLoad(il); il.Emit(OpCodes.Newarr, elem_type); il.Stloc(loc_array); Label for_end_label = il.DefineLabel(); Label condition_label = il.DefineLabel(); // i = 0; il.Emit(OpCodes.Ldc_I4_0); il.Stloc(loc_i); // FOR (i = 0; i < opt_arg_count; i++) if (true) { il.MarkLabel(condition_label); // condition (i < opt_arg_count): il.Ldloc(loc_i); optArgCount.EmitLoad(il); il.Emit(OpCodes.Bge, for_end_label); // LOAD stack, i + start+1>: builder.Stack.EmitLoad(il); il.Ldloc(loc_i); il.LdcI4(start + 1); il.Emit(OpCodes.Add); if (elem_type == typeof(PhpReference)) { // CALL stack.PeekReferenceUnchecked(STACK); il.Emit(OpCodes.Call, Methods.PhpStack.PeekReferenceUnchecked); actual_type = typeof(PhpReference); } else { // CALL stack.PeekValueUnchecked(STACK); il.Emit(OpCodes.Call, Methods.PhpStack.PeekValueUnchecked); actual_type = typeof(object); } // emits a conversion stuff (loads result into "elem" local variable): builder.EmitArgumentConversion(elem_type, actual_type, false, param); il.Stloc(loc_elem); // array[i] = elem; il.Ldloc(loc_array); il.Ldloc(loc_i); il.Ldloc(loc_elem); il.Stelem(elem_type); // i = i + 1; il.Ldloc(loc_i); il.Emit(OpCodes.Ldc_I4_1); il.Emit(OpCodes.Add); il.Stloc(loc_i); // GOTO condition; il.Emit(OpCodes.Br, condition_label); } // END FOR il.MarkLabel(for_end_label); // loads array to stack - consumed by the method call: il.Ldloc(loc_array); }
/// <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); }
private ArrayList refReferences = new ArrayList(3); // GENERICS: <LocalBuilder> #endregion #region Call Switch Emitter /// <summary> /// Emits calls to specified overloads and a switch statement which calls appropriate overload /// according to the current value of <see cref="PhpStack.ArgCount"/> field of the current stack. /// </summary> /// <param name="thisRef">Reference to self.</param> /// <param name="script_context">Current script context.</param> /// <param name="rtVariables"> /// Place where a run-time variables table can be loaded from. /// </param> /// <param name="namingContext">Naming context load-from place.</param> /// <param name="classContext">Class context load.</param> /// <param name="overloads">The overload list.</param> /// <remarks> /// Example: given overloads (2,5,7,9+), i.e. there are four overloads having 2, 5, 7 and 9 PHP parameters, /// respectively, and the last overload is marked as vararg, /// the method emits the following code: /// <code> /// switch(ArgCount - 2) // 2 = minimum { arg count of overload } /// { /// case 0: return call #2; // call to the 2nd overload with appropriate arg. and return value handling /// case 1: goto case error; /// case 2: goto case error; /// case 3: return call #5; /// case 4: goto case error; /// case 5: return call #7; /// case 6: goto case error; /// /// #if vararg /// case 7: goto default; /// default: return call #vararg (9 mandatory args,optional args);break; /// #elif /// case 7: return call #9; /// default: goto case error; /// #endif /// /// case error: PhpException.InvalidArgumentCount(null, functionName); break; /// } /// </code> /// </remarks> public void EmitCallSwitch(IPlace /*!*/ thisRef, IPlace /*!*/ script_context, IPlace /*!*/ rtVariables, IPlace /*!*/ namingContext, IPlace /*!*/ classContext, List <PhpLibraryFunction.Overload> /*!!*/ overloads) { Debug.AssertAllNonNull(overloads); int last = overloads.Count - 1; int min = overloads[0].ParamCount; int max = overloads[last].ParamCount; var flags = overloads[last].Flags; // if function is not supported, just throw the warning: if ((flags & PhpLibraryFunction.OverloadFlags.NotSupported) != 0) { // stack.RemoveFrame(); if (stack != null) { stack.EmitLoad(il); il.Emit(OpCodes.Call, Methods.PhpStack.RemoveFrame); } // PhpException.FunctionNotSupported( <FullName> ); il.Emit(OpCodes.Ldstr, FunctionName.Value); il.Emit(OpCodes.Call, Methods.PhpException.FunctionNotSupported_String); if (debug) { il.Emit(OpCodes.Nop); } // load methods default value il.EmitBoxing(OverloadsBuilder.EmitLoadDefault(il, overloads[last].Method)); return; } bool is_vararg = (flags & PhpLibraryFunction.OverloadFlags.IsVararg) != 0; if ((flags & PhpLibraryFunction.OverloadFlags.NeedsScriptContext) == 0) { script_context = null; } if ((flags & PhpLibraryFunction.OverloadFlags.NeedsThisReference) == 0) { thisRef = null; } if ((flags & PhpLibraryFunction.OverloadFlags.NeedsVariables) == 0) { rtVariables = null; } if ((flags & PhpLibraryFunction.OverloadFlags.NeedsNamingContext) == 0) { namingContext = null; } if ((flags & (PhpLibraryFunction.OverloadFlags.NeedsClassContext | PhpLibraryFunction.OverloadFlags.NeedsLateStaticBind)) == 0) { classContext = null; } Label end_label = il.DefineLabel(); Label error_label = il.DefineLabel(); Label[] cases = new Label[max - min + 1]; MethodInfo method; // fills cases with "goto case error": for (int i = 0; i < cases.Length; i++) { cases[i] = error_label; } // define labels for valid cases: for (int i = 0; i < overloads.Count; i++) { int count = overloads[i].ParamCount; cases[count - min] = il.DefineLabel(); } // LOAD(stack.ArgCount - min); stack.EmitLoad(il); il.Emit(OpCodes.Ldfld, Fields.PhpStack_ArgCount); il.LdcI4(min); il.Emit(OpCodes.Sub); // SWITCH(tmp) il.Emit(OpCodes.Switch, cases); // CASE >=N or <0 (underflows); // if the last overload is vararg: if (is_vararg) { LocalBuilder opt_arg_count_local = il.DeclareLocal(typeof(int)); // CASE N: il.MarkLabel(cases[cases.Length - 1]); // opt_arg_count = stack.ArgCount - max; stack.EmitLoad(il); il.Emit(OpCodes.Ldfld, Fields.PhpStack_ArgCount); il.LdcI4(max); il.Emit(OpCodes.Sub); il.Stloc(opt_arg_count_local); // IF(tmp<0) GOTO CASE error; il.Ldloc(opt_arg_count_local); il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Blt, error_label); // emits argument loading, stack frame removal, method call, return value conversion: method = overloads[last].Method; Type return_type = EmitOverloadCall(method, overloads[last].RealParameters, max, script_context, rtVariables, namingContext, classContext, new Place(opt_arg_count_local), thisRef, false); // loads boxed return value: if (return_type != Types.Void) { //il.LoadBoxed(return_value); if (return_type.IsValueType) { il.Emit(OpCodes.Box, return_type); } } else { il.Emit(OpCodes.Ldnull); } // RETURN; il.Emit(OpCodes.Ret); //bug in Reflector: il.Emit(OpCodes.Br,end_label); } else { // GOTO CASE error; il.Emit(OpCodes.Br, error_label); } // emits all valid cases which are not vararg: int j = 0; for (int i = min; i <= max - (is_vararg ? 1 : 0); i++) { if (overloads[j].ParamCount == i) { // CASE <i>; il.MarkLabel(cases[i - min]); // emits argument loading, stack frame removal, method call, return value conversion: method = overloads[j].Method; Type return_type = EmitOverloadCall(method, overloads[j].RealParameters, i, script_context, rtVariables, namingContext, classContext, null, thisRef, false); // loads boxed return value: if (return_type != Types.Void) { //il.LoadBoxed(return_value); if (return_type.IsValueType) { il.Emit(OpCodes.Box, return_type); } } else { il.Emit(OpCodes.Ldnull); } // RETURN; il.Emit(OpCodes.Ret); //bug in Reflector: il.Emit(OpCodes.Br,end_label); j++; } } Debug.Assert(j + (is_vararg ? 1 : 0) == overloads.Count); // ERROR: il.MarkLabel(error_label); il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Ldstr, this.functionName.ToString()); il.Emit(OpCodes.Call, Methods.PhpException.InvalidArgumentCount); if (debug) { il.Emit(OpCodes.Nop); } // RETURN null: il.Emit(OpCodes.Ldnull); il.MarkLabel(end_label); }
/// <summary> Implements method </summary> /// <remarks><code> /// class A : IDuck { /// /*type*/ Func(/*arguments*/) { /// sc = ScriptContext.Current; /// // temporary array is created only when arguments.Length > 8 (otherwise AddFrame overload exists) /// object[] tmp = new object[arguments.Length]; /// tmp[#i] = new PhpReference(PhpVariable.Copy(ClrObject.WrapDynamic(argument#i), CopyReason.PassedByCopy)); /// sc.Stack.AddFrame(tmp); /// return /* .. type conversion .. */ /// } /// } /// </code></remarks> private void ImplementMethod(TypeBuilder tb, MethodInfo method, FieldInfo fld, bool globalFuncs) { // get parameters (i want C# 3.0 NOW!!) ParameterInfo[] pinfo = method.GetParameters(); Type[] ptypes = new Type[pinfo.Length]; for (int i = 0; i < pinfo.Length; i++) { ptypes[i] = pinfo[i].ParameterType; } int argCount = pinfo.Length; string methName = method.Name; object[] attrs = method.GetCustomAttributes(typeof(DuckNameAttribute), false); if (attrs.Length > 0) { methName = ((DuckNameAttribute)attrs[0]).Name; } // define method MethodBuilder mb = tb.DefineMethod(method.Name, MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final, method.ReturnType, ptypes); ILEmitter il = new ILEmitter(mb); // Wrap parameters // sc = ScriptContext.Current LocalBuilder sc = il.DeclareLocal(typeof(ScriptContext)); il.Emit(OpCodes.Call, Methods.ScriptContext.GetCurrentContext); il.Stloc(sc); LocalBuilder ar = null; if (argCount > 8) { // tmp = new object[pinfo.Length]; ar = il.DeclareLocal(typeof(object[])); il.Emit(OpCodes.Ldc_I4, pinfo.Length); il.Emit(OpCodes.Newarr, typeof(object)); il.Stloc(ar); } // sc.Stack.AddFrame(...); il.Ldloc(sc); il.Load(Fields.ScriptContext_Stack); for (int i = 0; i < argCount; i++) { if (argCount > 8) { // tmp[i] il.Emit(OpCodes.Ldloc, ar); il.Emit(OpCodes.Ldc_I4, i); } // if (param#i is IDuckType) // param#i.OriginalObject // else // new PhpReference(PhpVariable.Copy(ClrObject.WrapDynamic(param#i), CopyReason.PassedByCopy)); Label lblDuckType = il.DefineLabel(); Label lblEnd = il.DefineLabel(); if (!ptypes[i].IsValueType) { il.Ldarg(i + 1); il.Emit(OpCodes.Isinst, typeof(IDuckType)); il.Emit(OpCodes.Brtrue, lblDuckType); } il.Ldarg(i + 1); if (ptypes[i].IsValueType) { il.Emit(OpCodes.Box, ptypes[i]); } il.Emit(OpCodes.Call, Methods.ClrObject_WrapDynamic); il.LdcI4((int)CopyReason.PassedByCopy); il.Emit(OpCodes.Call, Methods.PhpVariable.Copy); il.Emit(OpCodes.Newobj, Constructors.PhpReference_Object); if (!ptypes[i].IsValueType) { il.Emit(OpCodes.Br, lblEnd); il.MarkLabel(lblDuckType); il.Ldarg(i + 1); il.Emit(OpCodes.Call, typeof(IDuckType).GetProperty("OriginalObject").GetGetMethod()); il.MarkLabel(lblEnd); } if (argCount > 8) { il.Emit(OpCodes.Stelem_Ref); } } if (argCount > 8) { il.Emit(OpCodes.Ldloc, ar); } il.Emit(OpCodes.Call, Methods.PhpStack.AddFrame.Overload(argCount)); if (globalFuncs) { // localVariables = null, namingContext = null // ScriptContex.Call(null, null, "Foo", null, ScriptContext.Current).value; il.LoadLiteral(null); il.LoadLiteral(null); il.LoadLiteral(methName); il.LoadLiteral(null); il.Emit(OpCodes.Ldsflda, il.TypeBuilder.DefineField("<callHint>'lambda", typeof(PHP.Core.Reflection.DRoutineDesc), FieldAttributes.Static | FieldAttributes.Private)); il.Ldloc(sc); il.Emit(OpCodes.Call, Methods.ScriptContext.Call); } else { // Operators.InvokeMethod(this.obj, "Foo", null, ScriptContext.Current).value; il.Ldarg(0); il.Load(fld); il.LoadLiteral(methName); il.LoadLiteral(null); il.Ldloc(sc); il.Emit(OpCodes.Call, Methods.Operators.InvokeMethodStr); } EmitReturn(il, method.ReturnType, true); tb.DefineMethodOverride(mb, method); }
/// <summary> Emit PHP to CLR conversion </summary> /// <remarks>If the return type is interface marked using <seealso cref="DuckTypeAttribute"/> /// it is wrapped again. /// <code> /// // type is IDuckEnumerable<T> /// return new DuckEnumerableWrapper<T>(obj.GetForeachEnumerator(false, false, null)) /// /// // type is IDuckKeyedEnumerable<T> /// return new DuckKeyedEnumerableWrapper<T>(obj.GetForeachEnumerator(true, false, null)) /// /// // type is marked using [DuckType] /// return DuckTyping.Instance.ImplementDuckType<T>(obj); /// /// // otherwise uses standard ConvertToClr conversion method /// </code> /// </remarks> private static void EmitReturn(ILEmitter il, Type returnedType, bool isPhpRef) { Type[] gargs = returnedType.GetGenericArguments(); object[] attrs = returnedType.GetCustomAttributes(typeof(DuckTypeAttribute), false); bool isDuckEnumerable = (gargs.Length == 1 && returnedType.Equals(typeof(IDuckEnumerable <>).MakeGenericType(gargs))); bool isDuckKeyedEnumerable = (gargs.Length == 2 && returnedType.Equals(typeof(IDuckKeyedEnumerable <,>).MakeGenericType(gargs))); bool isDuckType = attrs != null && attrs.Length > 0; if (returnedType.Equals(typeof(void))) { il.Emit(OpCodes.Pop); il.Emit(OpCodes.Ret); } else if (isDuckType || isDuckEnumerable || isDuckKeyedEnumerable) { LocalBuilder tmp = il.DeclareLocal(typeof(object)); //store the value local var (after unwrapping it from the reference) if (isPhpRef) { il.Emit(OpCodes.Ldfld, Fields.PhpReference_Value); } il.Stloc(tmp); Label lblTestMinusOne = il.DefineLabel(); Label lblWrap = il.DefineLabel(); Label lblInvalidInt = il.DefineLabel(); // test whether the value is null il.Ldloc(tmp); il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Ceq); il.Emit(OpCodes.Brfalse, lblTestMinusOne); il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Ret); il.MarkLabel(lblTestMinusOne); // test whether value is -1 il.Ldloc(tmp); il.Emit(OpCodes.Isinst, typeof(int)); il.Emit(OpCodes.Brfalse, lblWrap); // value is not int, so we can wrap the value il.Ldloc(tmp); il.Emit(OpCodes.Unbox_Any, typeof(int)); il.Emit(OpCodes.Ldc_I4, -1); il.Emit(OpCodes.Ceq); il.Emit(OpCodes.Brfalse, lblWrap); // value is int but not -1 il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Ret); il.MarkLabel(lblWrap); // specific duck type wrapping if (isDuckEnumerable || isDuckKeyedEnumerable) { il.Ldloc(tmp); il.Emit(OpCodes.Dup); // Standard: new DuckEnumerableWrapper<T>(obj.GetForeachEnumerator(false, false, null)) // Keyed: new DuckKeyedEnumerableWrapper<T>(obj.GetForeachEnumerator(false, false, null)) il.LoadLiteral(gargs.Length == 2); // keyed? il.LoadLiteral(false); il.LoadLiteral(null); il.Emit(OpCodes.Callvirt, Methods.IPhpEnumerable_GetForeachEnumerator); if (isDuckEnumerable) { il.Emit(OpCodes.Newobj, typeof(DuckEnumerableWrapper <>). MakeGenericType(gargs).GetConstructors()[0]); } else { il.Emit(OpCodes.Newobj, typeof(DuckKeyedEnumerableWrapper <,>). MakeGenericType(gargs).GetConstructors()[0]); } } else { il.Emit(OpCodes.Call, typeof(DuckTyping).GetProperty("Instance", BindingFlags.Public | BindingFlags.Static).GetGetMethod()); il.Ldloc(tmp); il.Emit(OpCodes.Call, typeof(DuckTyping).GetMethod("ImplementDuckType", BindingFlags.Public | BindingFlags.Instance).MakeGenericMethod(returnedType)); } il.Emit(OpCodes.Ret); } else { if (returnedType == typeof(object)) { Label lbl = il.DefineLabel(); if (isPhpRef) { il.Emit(OpCodes.Ldfld, Fields.PhpReference_Value); } il.Emit(OpCodes.Dup); il.Emit(OpCodes.Isinst, typeof(PhpBytes)); il.Emit(OpCodes.Brfalse, lbl); il.EmitCall(OpCodes.Call, typeof(IPhpConvertible).GetMethod("ToString", Type.EmptyTypes), Type.EmptyTypes); il.Emit(OpCodes.Ret); il.MarkLabel(lbl); ClrOverloadBuilder.EmitConvertToClr(il, PhpTypeCode.Object, returnedType); il.Emit(OpCodes.Ret); } else { ClrOverloadBuilder.EmitConvertToClr(il, isPhpRef ? PhpTypeCode.PhpReference : PhpTypeCode.Object, returnedType); il.Emit(OpCodes.Ret); } } }
/// <summary> /// Emits a static inclusion. /// </summary> private PhpTypeCode EmitStaticInclusion(CodeGenerator /*!*/ codeGenerator) { ILEmitter il = codeGenerator.IL; Label endif_label = il.DefineLabel(); Label else_label = il.DefineLabel(); MethodInfo method; // if the expression should be emitted: if (characteristic == Characteristic.StaticArgEvaluated) { if (!(fileNameEx is StringLiteral || fileNameEx is BinaryStringLiteral)) { // emits expression evaluation and ignores the result: fileNameEx.Emit(codeGenerator); il.Emit(OpCodes.Pop); } } if (characteristic == Characteristic.StaticAutoInclusion) { // calls the Main routine only if this script is the main one: il.Ldarg(ScriptBuilder.ArgIsMain); } else { RelativePath relativePath = new RelativePath(inclusion.Includee.RelativeSourcePath); // normalize the relative path // CALL context.StaticInclude(<relative included script source path>,<this script type>,<inclusion type>); codeGenerator.EmitLoadScriptContext(); il.Emit(OpCodes.Ldc_I4, (int)relativePath.Level); il.Emit(OpCodes.Ldstr, relativePath.Path); il.Emit(OpCodes.Ldtoken, inclusion.Includee.ScriptClassType); il.LoadLiteral(inclusionType); il.Emit(OpCodes.Call, Methods.ScriptContext.StaticInclude); } // IF (STACK) il.Emit(OpCodes.Brfalse, else_label); if (true) { // emits a call to the main helper of the included script: method = inclusion.Includee.MainHelper; // CALL <Main>(context, variables, self, includer, false): codeGenerator.EmitLoadScriptContext(); codeGenerator.EmitLoadRTVariablesTable(); codeGenerator.EmitLoadSelf(); codeGenerator.EmitLoadClassContext(); il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Call, method); il.Emit(OpCodes.Br, endif_label); } // ELSE il.MarkLabel(else_label); if (true) { // LOAD <PhpScript.SkippedIncludeReturnValue>; il.LoadLiteral(ScriptModule.SkippedIncludeReturnValue); il.Emit(OpCodes.Box, ScriptModule.SkippedIncludeReturnValue.GetType()); } il.MarkLabel(endif_label); // END IF return(PhpTypeCode.Object); }
/// <summary> /// Emits IL instructions that unset an instance field. /// </summary> /// <remarks> /// Nothing is expected on the evaluation stack. Nothing is left on the evaluation stack. /// </remarks> private void EmitUnsetField(CodeGenerator /*!*/ codeGenerator) { ILEmitter il = codeGenerator.IL; DirectVarUse direct_instance = isMemberOf as DirectVarUse; if (direct_instance != null && direct_instance.VarName.IsThisVariableName) { // $this->a switch (codeGenerator.LocationStack.LocationType) { case LocationTypes.GlobalCode: { // load $this from one of Main's arguments and check for null Label this_non_null = il.DefineLabel(); codeGenerator.EmitLoadSelf(); il.Emit(OpCodes.Brtrue_S, this_non_null); codeGenerator.EmitPhpException(Methods.PhpException.ThisUsedOutOfObjectContext); il.Emit(OpCodes.Br, codeGenerator.ChainBuilder.ErrorLabel); il.MarkLabel(this_non_null, true); // call UnsetProperty codeGenerator.EmitLoadSelf(); il.Emit(OpCodes.Ldstr, varName.ToString()); // TODO codeGenerator.EmitLoadClassContext(); il.EmitCall(OpCodes.Call, Methods.Operators.UnsetProperty, null); return; } case LocationTypes.FunctionDecl: { // always throws error codeGenerator.EmitPhpException(Methods.PhpException.ThisUsedOutOfObjectContext); il.Emit(OpCodes.Br, codeGenerator.ChainBuilder.ErrorLabel); break; } case LocationTypes.MethodDecl: { CompilerLocationStack.MethodDeclContext context = codeGenerator.LocationStack.PeekMethodDecl(); if (context.Method.IsStatic) { // always throws error codeGenerator.EmitPhpException(Methods.PhpException.ThisUsedOutOfObjectContext); il.Emit(OpCodes.Br, codeGenerator.ChainBuilder.ErrorLabel); } else { DProperty property; if (context.Type.GetProperty(varName, context.Type, out property) == GetMemberResult.OK && !property.IsStatic) { // ask the DProperty to emit its unsetting code property.EmitUnset(codeGenerator, IndexedPlace.ThisArg, null, false); } else { // unable to resolve the field -> call UnsetProperty codeGenerator.EmitLoadSelf(); il.Emit(OpCodes.Ldstr, varName.ToString()); codeGenerator.EmitLoadClassContext(); il.EmitCall(OpCodes.Call, Methods.Operators.UnsetProperty, null); } } } break; } } else { // call UnsetProperty isMemberOf.Emit(codeGenerator); il.Emit(OpCodes.Ldstr, varName.ToString()); codeGenerator.EmitLoadClassContext(); il.EmitCall(OpCodes.Call, Methods.Operators.UnsetProperty, null); } }
/// <summary> /// Emits local variable switch and performs a specified operation on each case. /// </summary> /// <param name="codeGenerator">The code generator.</param> /// <param name="method">The operation performed in each case.</param> internal void EmitSwitch(CodeGenerator codeGenerator, SwitchMethod method) { ILEmitter il = codeGenerator.IL; Debug.Assert(method != null); Label default_case = il.DefineLabel(); Label end_label = il.DefineLabel(); LocalBuilder ivar_local = il.GetTemporaryLocal(Types.String[0], true); LocalBuilder non_interned_local = il.DeclareLocal(Types.String[0]); VariablesTable variables = codeGenerator.CurrentVariablesTable; Label[] labels = new Label[variables.Count]; // non_interned_local = <name expression>; EmitName(codeGenerator); il.Stloc(non_interned_local); // ivar_local = String.IsInterned(non_interned_local) il.Ldloc(non_interned_local); il.Emit(OpCodes.Call, Methods.String_IsInterned); il.Stloc(ivar_local); // switch for every compile-time variable: int i = 0; foreach (VariablesTable.Entry variable in variables) { labels[i] = il.DefineLabel(); // IF (ivar_local == <i-th variable name>) GOTO labels[i]; il.Ldloc(ivar_local); il.Emit(OpCodes.Ldstr, variable.VariableName.ToString()); il.Emit(OpCodes.Beq, labels[i]); i++; } // GOTO default_case: il.Emit(OpCodes.Br, default_case); // operation on each variable: i = 0; foreach (VariablesTable.Entry variable in variables) { // labels[i]: il.MarkLabel(labels[i]); // operation: method(codeGenerator, variable, null); // GOTO end; il.Emit(OpCodes.Br, end_label); i++; } // default case - new variable created at runtime: il.MarkLabel(default_case); method(codeGenerator, null, non_interned_local); // END: il.MarkLabel(end_label); }
/// <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 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); } }