コード例 #1
0
ファイル: ClrStubBuilder.cs プロジェクト: jiahao42/weverca
        /// <summary>
        /// Emits code that loads a specified parameter on the evaluation stack.
        /// </summary>
        /// <param name="paramInfo">The parameter to load.</param>
        /// <param name="requiredTypeCode">Specifies whether <see cref="PhpReference"/>
        /// (<see cref="PhpTypeCode.PhpReference"/>), <see cref="object"/> (<see cref="PhpTypeCode.Object"/>),
        /// or the most fitting of these two should be loaded.</param>
        public void EmitLoadClrParameter(ParameterInfo /*!*/ paramInfo, PhpTypeCode requiredTypeCode)
        {
            if (paramInfo.IsOut)
            {
                il.Emit(OpCodes.Ldnull);
            }
            else
            {
                il.Ldarg(paramInfo.Position + paramOffset);

                // dereference ref param
                Type param_type = paramInfo.ParameterType;
                if (param_type.IsByRef)
                {
                    param_type = param_type.GetElementType();

                    il.Ldind(param_type);
                }

                // convert the parameter to PHP type
                PhpTypeCode type_code = ClrOverloadBuilder.EmitConvertToPhp(
                    il,
                    param_type /*,
                                * scriptContextPlace*/);

                il.EmitBoxing(type_code);
            }

            // check whether we have to create a PhpReference
            if (requiredTypeCode == PhpTypeCode.Object ||
                (requiredTypeCode == PhpTypeCode.Unknown && !paramInfo.ParameterType.IsByRef))
            {
                return;
            }

            if (paramInfo.ParameterType.IsByRef)
            {
                LocalBuilder ref_local = il.DeclareLocal(Types.PhpReference[0]);

                // remember the PhpReference in a local
                il.Emit(OpCodes.Newobj, Constructors.PhpReference_Object);
                il.Emit(OpCodes.Dup);
                il.Stloc(ref_local);

                referenceLocals[paramInfo.Position] = ref_local;
            }
            else
            {
                // no reference store-back is necessary
                il.Emit(OpCodes.Newobj, Constructors.PhpReference_Object);
            }
        }
コード例 #2
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);
        }
コード例 #3
0
ファイル: AssignEx.cs プロジェクト: ikvm/Phalanger
        /// <summary>
        /// Emits assignment.
        /// </summary>
        /// <remarks>
        /// Pattern: a op= b
        ///
        /// PREPARE a      (prepared)
        /// LOAD a         (prepared,a)
        /// LOAD b         (prepared,a,b)
        /// OP             (prepared,result)
        /// *DUP           (prepared,result,result)
        /// *STORE tmp     (prepared,result)           must be this stack here!
        /// STORE a        ()
        /// *LOAD tmp      (result)
        ///
        /// * only if the resulting value needs to be propagated to the right
        ///
        /// Note: There is a possible improvement: some store operations (SetVariable) may return the value set
        /// which would replace DUP and second temp op.
        /// </remarks>
        internal override PhpTypeCode Emit(CodeGenerator /*!*/ codeGenerator)
        {
            Debug.Assert(access == AccessType.Read || access == AccessType.None || access == AccessType.ReadRef ||
                         access == AccessType.ReadUnknown);
            Statistics.AST.AddNode("Assign.Value");

            ILEmitter il = codeGenerator.IL;

            AccessType old_selector = codeGenerator.AccessSelector;

            codeGenerator.ChainBuilder.Create();

            PhpTypeCode result;

            if (operation == Operations.AssignValue)
            {
                //
                // Access Type = ReadRef/ReadUnknown
                // ---------------------------------
                //
                // f(&$x) { }
                //
                // f($a = $b);
                // f($a = $b =& $c);
                //
                // Destination variable $a is prepared for reference write.
                // A new reference is created and its value set to a deep copy of the result of RHS ($b, $b =& $c).
                // RHS has Read access => it has been dereferenced.
                //

                // PREPARE a:
                codeGenerator.AccessSelector = AccessType.Write;
                lvalue.Emit(codeGenerator);
                codeGenerator.AccessSelector = AccessType.None;

                PhpTypeCode src_type_code = EmitSourceValRead(codeGenerator);

                // RHS should have Read access => should be dereferenced
                Debug.Assert(src_type_code != PhpTypeCode.PhpReference);

                // LOAD BOX b
                codeGenerator.EmitBoxing(src_type_code);

                // makes a copy if necessary:
                if (PhpTypeCodeEnum.IsDeeplyCopied(src_type_code))
                {
                    codeGenerator.EmitVariableCopy(CopyReason.Assigned, rvalue);
                }
            }
            else
            {
                // PREPARE a:
                codeGenerator.AccessSelector = AccessType.Write;
                lvalue.Emit(codeGenerator);
                codeGenerator.AccessSelector = AccessType.None;

                // LOAD b,a (rvalue must be processed first, than +-*/ with lvalue, since lvalu can be changed by rvalue expression)
                //must be the second operand// EmitDestVarRead(codeGenerator);
                PhpTypeCode right_type = EmitSourceValRead(codeGenerator);
                var         rvalue_tmp = codeGenerator.IL.GetTemporaryLocal(PhpTypeCodeEnum.ToType(right_type), false);
                codeGenerator.IL.Emit(OpCodes.Stloc, rvalue_tmp);
                EmitDestVarRead(codeGenerator);
                codeGenerator.IL.Emit(OpCodes.Ldloc, rvalue_tmp);
                codeGenerator.IL.ReturnTemporaryLocal(rvalue_tmp);

                switch (operation)
                {
                    #region Arithmetic

                case Operations.AssignAdd:
                {
                    switch (right_type)
                    {
                    case PhpTypeCode.Integer:
                        result = codeGenerator.EmitMethodCall(Methods.Operators.Add.Object_Int32);
                        break;

                    case PhpTypeCode.Double:
                        result = codeGenerator.EmitMethodCall(Methods.Operators.Add.Object_Double);
                        break;

                    default:
                        codeGenerator.EmitBoxing(right_type);
                        result = codeGenerator.EmitMethodCall(Methods.Operators.Add.Object_Object);
                        break;
                    }
                    break;
                }

                case Operations.AssignSub:
                {
                    switch (right_type)
                    {
                    case PhpTypeCode.Integer:
                        result = codeGenerator.EmitMethodCall(Methods.Operators.Subtract.Object_Int);
                        break;

                    default:
                        codeGenerator.EmitBoxing(right_type);
                        result = codeGenerator.EmitMethodCall(Methods.Operators.Subtract.Object_Object);
                        break;
                    }
                    break;
                }

                case Operations.AssignDiv:
                {
                    switch (right_type)
                    {
                    case PhpTypeCode.Integer:
                        result = codeGenerator.EmitMethodCall(Methods.Operators.Divide.Object_Int32);
                        break;

                    case PhpTypeCode.Double:
                        result = codeGenerator.EmitMethodCall(Methods.Operators.Divide.Object_Double);
                        break;

                    default:
                        codeGenerator.EmitBoxing(right_type);
                        result = codeGenerator.EmitMethodCall(Methods.Operators.Divide.Object_Object);
                        break;
                    }
                    break;
                }

                case Operations.AssignMul:
                {
                    switch (right_type)
                    {
                    case PhpTypeCode.Integer:
                        result = codeGenerator.EmitMethodCall(Methods.Operators.Multiply.Object_Int32);
                        break;

                    case PhpTypeCode.Double:
                        result = codeGenerator.EmitMethodCall(Methods.Operators.Multiply.Object_Double);
                        break;

                    default:
                        codeGenerator.EmitBoxing(right_type);
                        result = codeGenerator.EmitMethodCall(Methods.Operators.Multiply.Object_Object);
                        break;
                    }
                    break;
                }

                case Operations.AssignMod:

                    if (right_type == PhpTypeCode.Integer)
                    {
                        result = codeGenerator.EmitMethodCall(Methods.Operators.Remainder.Object_Int32);
                    }
                    else
                    {
                        codeGenerator.EmitBoxing(right_type);
                        result = codeGenerator.EmitMethodCall(Methods.Operators.Remainder.Object_Object);
                    }
                    break;


                    #endregion

                    #region Bitwise

                case Operations.AssignAnd:
                    codeGenerator.EmitBoxing(right_type);
                    il.Emit(OpCodes.Ldc_I4, (int)Operators.BitOp.And);
                    result = codeGenerator.EmitMethodCall(Methods.Operators.BitOperation);
                    break;

                case Operations.AssignOr:
                    codeGenerator.EmitBoxing(right_type);
                    il.Emit(OpCodes.Ldc_I4, (int)Operators.BitOp.Or);
                    result = codeGenerator.EmitMethodCall(Methods.Operators.BitOperation);
                    break;

                case Operations.AssignXor:
                    codeGenerator.EmitBoxing(right_type);
                    il.Emit(OpCodes.Ldc_I4, (int)Operators.BitOp.Xor);
                    result = codeGenerator.EmitMethodCall(Methods.Operators.BitOperation);
                    break;

                case Operations.AssignShiftLeft:
                    codeGenerator.EmitBoxing(right_type);
                    result = codeGenerator.EmitMethodCall(Methods.Operators.ShiftLeft);
                    break;

                case Operations.AssignShiftRight:
                    codeGenerator.EmitBoxing(right_type);
                    result = codeGenerator.EmitMethodCall(Methods.Operators.ShiftRight);
                    break;

                    #endregion

                    #region String

                case Operations.AssignAppend:
                {
                    if (right_type == PhpTypeCode.String)
                    {
                        result = codeGenerator.EmitMethodCall(Methods.Operators.Append.Object_String);
                    }
                    else if (right_type == PhpTypeCode.PhpBytes)
                    {
                        result = codeGenerator.EmitMethodCall(Methods.PhpBytes.Append_Object_PhpBytes);
                    }
                    else
                    {
                        codeGenerator.EmitBoxing(right_type);
                        result = codeGenerator.EmitMethodCall(Methods.Operators.Append.Object_Object);
                    }
                    break;
                }

                case Operations.AssignPrepend:
                {
                    if (right_type == PhpTypeCode.String)
                    {
                        result = codeGenerator.EmitMethodCall(Methods.Operators.Prepend.Object_String);
                    }
                    else
                    {
                        codeGenerator.EmitBoxing(right_type);
                        result = codeGenerator.EmitMethodCall(Methods.Operators.Prepend.Object_Object);
                    }
                    break;
                }

                    #endregion

                default:
                    Debug.Fail();
                    throw null;
                }

                il.EmitBoxing(result);
            }

            switch (access)
            {
            case AccessType.Read:
            {
                // DUP
                il.Emit(OpCodes.Dup);

                // STORE tmp
                il.Stloc(il.GetAssignmentLocal());

                // STORE prepared, result
                codeGenerator.AccessSelector = AccessType.Write;
                result = lvalue.EmitAssign(codeGenerator);
                codeGenerator.AccessSelector = AccessType.None;
                Debug.Assert(result == PhpTypeCode.Void);

                // LOAD result
                il.Ldloc(il.GetAssignmentLocal());

                result = PhpTypeCode.Object;
                break;
            }

            case AccessType.ReadRef:
            case AccessType.ReadUnknown:

                // STORE prepared,result
                codeGenerator.AccessSelector = AccessType.Write;
                result = lvalue.EmitAssign(codeGenerator);
                codeGenerator.AccessSelector = AccessType.None;
                Debug.Assert(result == PhpTypeCode.Void);

                // loads a reference on the LHS variable:
                codeGenerator.AccessSelector = access;
                codeGenerator.ChainBuilder.Create();
                result = lvalue.Emit(codeGenerator);
                codeGenerator.ChainBuilder.EndRef();
                codeGenerator.AccessSelector = AccessType.None;
                break;

            case AccessType.None:

                // STORE a:
                codeGenerator.AccessSelector = AccessType.Write;
                result = lvalue.EmitAssign(codeGenerator);
                codeGenerator.AccessSelector = AccessType.None;
                Debug.Assert(result == PhpTypeCode.Void);

                break;

            default:
                Debug.Fail("Invalid access type.");
                result = PhpTypeCode.Invalid;
                break;
            }

            codeGenerator.ChainBuilder.End();

            codeGenerator.AccessSelector = old_selector;

            return(result);
        }