Пример #1
0
            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);
            }
Пример #2
0
            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);
            }
Пример #3
0
        /// <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);
        }
Пример #4
0
        /// <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);
            }
        }
Пример #5
0
        /// <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);
        }
Пример #6
0
        /// <summary>
        /// Emits init field helpers (<c>__lastContext</c> field, <c>&lt;InitializeInstanceFields&gt;</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
            }
        }
Пример #7
0
        /// <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);
        }
Пример #8
0
        /// <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);
        }
Пример #9
0
        /// <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);
        }
Пример #10
0
        /// <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);
        }
Пример #11
0
        /// <summary>
        /// Define new instance of CallSite&lt;<paramref name="delegateType"/>&gt; 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);
        }
Пример #12
0
            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);
            }
Пример #13
0
        /// <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);
        }
Пример #14
0
        /// <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);
        }
Пример #15
0
        /// <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--;
        }
Пример #16
0
            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);
            }
Пример #17
0
        /// <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);
        }
Пример #18
0
        /// <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);
        }
Пример #19
0
        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);
        }
Пример #20
0
        /// <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);
        }
Пример #21
0
        /// <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&lt;T&gt;
        /// return new DuckEnumerableWrapper&lt;T&gt;(obj.GetForeachEnumerator(false, false, null))
        ///
        /// // type is IDuckKeyedEnumerable&lt;T&gt;
        /// return new DuckKeyedEnumerableWrapper&lt;T&gt;(obj.GetForeachEnumerator(true, false, null))
        ///
        /// // type is marked using [DuckType]
        /// return DuckTyping.Instance.ImplementDuckType&lt;T&gt;(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);
                }
            }
        }
Пример #22
0
        /// <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);
        }
Пример #23
0
        /// <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);
            }
        }
Пример #24
0
        /// <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);
        }
Пример #25
0
        /// <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);
        }
Пример #26
0
        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);
            }
        }