Ejemplo n.º 1
0
        public MethodInfo EmitLambda(string name, AST.DirectVarUse variable, AST.Expression /*!*/ expression,
                                     PhpTypeCode returnType)
        {
            MethodBuilder result = linqContextBuilder.DefineMethod(name, MethodAttributes.PrivateScope | MethodAttributes.SpecialName,
                                                                   PhpTypeCodeEnum.ToType(returnType), new Type[] { typeof(object) });

            ILEmitter il = new ILEmitter(result);

            EnterLambdaDeclaration(il);

            if (variable != null)
            {
                // <variable> = COPY(<argument>);
                variable.Emit(cg);
                il.Emit(OpCodes.Ldarg_1);
                cg.EmitVariableCopy(CopyReason.Assigned, null);
                variable.EmitAssign(cg);
            }

            cg.EmitConversion(expression, returnType);
            il.Emit(OpCodes.Ret);

            LeaveLambdaDeclaration();

            return(result);
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Emits parameter loading.
        /// </summary>
        /// <param name="il">Emitter.</param>
        /// <param name="index">The index of the parameter starting from 0.</param>
        /// <param name="codeGenerator">Code generator.</param>
        /// <returns>The type of the actual argument or its value if it is a leteral.</returns>
        internal object EmitLibraryLoadArgument(ILEmitter /*!*/ il, int index, object /*!*/ codeGenerator)
        {
            Debug.Assert(codeGenerator != null);
            Debug.Assert(index < parameters.Count, "Missing arguments prevents code generation");

            // returns value if the parameter is evaluable at compile time:
            if (parameters[index].Expression.HasValue)
            {
                return(parameters[index].Expression.Value);
            }

            // emits parameter evaluation:
            return(PhpTypeCodeEnum.ToType(parameters[index].Emit((CodeGenerator)codeGenerator)));
        }
Ejemplo n.º 3
0
            /// <summary>
            /// Emits parameter loading.
            /// </summary>
            /// <param name="node">Instance.</param>
            /// <param name="il">Emitter.</param>
            /// <param name="index">The index of the parameter starting from 0.</param>
            /// <param name="codeGenerator">Code generator.</param>
            /// <param name="param">Target <see cref="ParameterInfo"/>.</param>
            /// <returns>The type of the actual argument or its value if it is a leteral.</returns>
            public object EmitLibraryLoadArgument(CallSignature /*!*/ node, ILEmitter /*!*/ il, int index, object /*!*/ codeGenerator, ParameterInfo param)
            {
                Debug.Assert(codeGenerator != null);
                Debug.Assert(index < node.Parameters.Length, "Missing arguments prevents code generation");

                // returns value if the parameter is evaluable at compile time:
                if (node.Parameters[index].Expression.HasValue())
                {
                    return(node.Parameters[index].Expression.GetValue());
                }

                // emits parameter evaluation:
                var p = node.Parameters[index];

                return(PhpTypeCodeEnum.ToType(p.NodeCompiler <ActualParamCompiler>().Emit(p, (CodeGenerator)codeGenerator, PhpRwAttribute.IsDefined(param))));
            }
Ejemplo n.º 4
0
        internal override PhpTypeCode EmitGet(CodeGenerator /*!*/ codeGenerator, ConstructedType constructedType,
                                              bool runtimeVisibilityCheck, string fallbackName)
        {
            ILEmitter il = codeGenerator.IL;

            if (HasValue)
            {
                il.LoadLiteral(Value);
                return(PhpTypeCodeEnum.FromObject(Value));
            }
            else
            {
                Debug.Assert(realField != null);

                il.Emit(OpCodes.Ldsfld, DType.MakeConstructed(realField, constructedType));
                return(PhpTypeCodeEnum.FromType(realField.FieldType));
            }
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Create and call <see cref="CallSite"/> for getting property.
        /// </summary>
        /// <param name="cg"><see cref="CodeGenerator"/>.</param>
        /// <param name="wantRef">Wheter <see cref="PhpReference"/> is expected as the result.</param>
        /// <param name="targetExpr">The expression representing the target (object).</param>
        /// <param name="targetObjectPlace">The place representing the target (<see cref="DObject"/>) iff <paramref name="targetExpr"/> is not provided.</param>
        /// <param name="targetPlace">The place representing the target (object) iff <paramref name="targetExpr"/> and <paramref name="targetObjectPlace"/> are not provided.</param>
        /// <param name="targetType">Type of target iff we are getting property statically.</param>
        /// <param name="fieldName">The name of the field. Can be null if the name is not known at compile time (indirect).</param>
        /// <param name="fieldNameExpr">The expression used to get field name in run time (iff <paramref name="fieldName"/> is <c>null</c>.</param>
        /// <param name="issetSemantics">Wheter we are only checking if the property exists. If true, no warnings are thrown during run time.</param>
        /// <returns>Type code of the value that is pushed onto the top of the evaluation stack.</returns>
        public PhpTypeCode EmitGetProperty(
            PHP.Core.CodeGenerator /*!*/ cg, bool wantRef,
            Expression targetExpr, IPlace targetObjectPlace, IPlace targetPlace, DType targetType,
            string fieldName, Expression fieldNameExpr,
            bool issetSemantics)
        {
            Debug.Assert(fieldName != null ^ fieldNameExpr != null);
            Debug.Assert(targetExpr != null || targetObjectPlace != null || targetPlace != null || targetType != null);

            //
            bool staticCall          = (targetExpr == null && targetObjectPlace == null && targetPlace == null); // we are going to access static property
            bool fieldNameIsKnown    = (fieldName != null);
            bool classContextIsKnown = (this.classContextPlace != null);

            //
            // binder flags:
            //
            Type returnType = wantRef ? Types.PhpReference[0] : Types.Object[0];

            //
            // define the call site:
            //

            //
            List <Type> additionalArgs = new List <Type>();

            if (!classContextIsKnown)
            {
                additionalArgs.Add(Types.DTypeDesc[0]);
            }
            if (!fieldNameIsKnown)
            {
                additionalArgs.Add(Types.String[0]);
            }

            var delegateTypeArgs = GetPropertyDelegateTypeArgs(
                staticCall ? Types.DTypeDesc[0] : ((targetObjectPlace != null) ? Types.DObject[0] : Types.Object[0]),   // DTypeDesc of static field's declaring type || DObject if field called on DObject known at compile time || otherwise object
                additionalArgs.ToArray(),
                returnType);

            var delegateType = /*System.Linq.Expressions.Expression.*/ delegateBuilder.GetDelegateType(delegateTypeArgs, callSitesCount);    // (J) do not create dynamic delegates in dynamic modules, so they can be referenced from non-transient assemblies

            //
            var field = DefineCallSite(cg.IL, string.Format("get{0}_{1}", wantRef ? "ref" : string.Empty, fieldName ?? "$"), delegateType, (il) =>
            {
                // <LOAD> Binder.{GetProperty|GetStaticProperty}( fieldName, classContext, issetSemantics, <returnType> )
                if (fieldName != null)
                {
                    il.Emit(OpCodes.Ldstr, fieldName);
                }
                else
                {
                    il.Emit(OpCodes.Ldnull);
                }
                if (this.classContextPlace != null)
                {
                    this.classContextPlace.EmitLoad(il);
                }
                else
                {
                    il.Emit(OpCodes.Ldsfld, Fields.UnknownTypeDesc.Singleton);
                }
                il.LoadBool(issetSemantics);

                il.Emit(OpCodes.Ldtoken, returnType);
                il.Emit(OpCodes.Call, Methods.GetTypeFromHandle);

                il.Emit(OpCodes.Call, staticCall ? Methods.Binder.StaticGetProperty : Methods.Binder.GetProperty);
            });

            //
            // call the CallSite:
            //

            // <field>.Target( <field>, <targetExpr|targetType>, (classContext)?, <methodNameExpr>? ):

            cg.IL.Emit(OpCodes.Ldsfld, field);
            cg.IL.Emit(OpCodes.Ldfld, field.FieldType.GetField("Target"));
            cg.IL.Emit(OpCodes.Ldsfld, field);
            if (staticCall)
            {
                targetType.EmitLoadTypeDesc(cg, ResolveTypeFlags.UseAutoload | ResolveTypeFlags.ThrowErrors);
            }
            else if (targetExpr != null)
            {
                cg.ChainBuilder.Lengthen();         // for hop over ->
                cg.EmitBoxing(targetExpr.Emit(cg)); // prepare for operator invocation
            }
            else if (targetObjectPlace != null)
            {
                targetObjectPlace.EmitLoad(cg.IL);
            }
            else if (targetPlace != null)
            {
                targetPlace.EmitLoad(cg.IL);
            }
            else
            {
                Debug.Fail();
            }
            if (!classContextIsKnown)
            {
                cg.EmitLoadClassContext();
            }
            if (!fieldNameIsKnown)
            {
                cg.EmitName(fieldName /*null*/, fieldNameExpr, true, PhpTypeCode.String);
            }

            cg.MarkTransientSequencePoint();
            cg.IL.Emit(OpCodes.Callvirt, delegateType.GetMethod("Invoke"));

            cg.MarkTransientSequencePoint();

            //
            return(PhpTypeCodeEnum.FromType(returnType));
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Emit call of the instance/static method. This defines the call site and call it using given parameters.
        /// </summary>
        /// <param name="cg">Current code <see cref="CodeGenerator"/>.</param>
        /// <param name="returnType">Return type of the method call determined by current access of the method call.</param>
        /// <param name="targetExpr">The method call instance expression (the target) if it is an instance method call.</param>
        /// <param name="targetType">The target type if it is a static method call.</param>
        /// <param name="methodFullName">If known at compile time, the method name. Otherwise <c>null</c>.</param>
        /// <param name="methodNameExpr">If the <paramref name="methodFullName"/> is null, this will be the expression giving the method name in run time.</param>
        /// <param name="callSignature">The call signature of the method call.</param>
        /// <returns>The resulting value type code. This value will be pushed onto the evaluation stack.</returns>
        public PhpTypeCode EmitMethodCall(
            PHP.Core.CodeGenerator /*!*/ cg, Type returnType,
            Expression /*!*/ targetExpr, DType /*!*/ targetType,
            string methodFullName, Expression methodNameExpr, CallSignature callSignature)
        {
            Debug.Assert(methodFullName != null ^ methodNameExpr != null);

            //
            bool staticCall = (targetExpr == null); // we are going to emit static method call
            //bool methodNameIsKnown = (methodFullName != null);
            //bool classContextIsKnown = (this.classContextPlace != null);

            //
            // define the call site:
            //
            var delegateType = /*System.Linq.Expressions.Expression.*/ delegateBuilder.GetDelegateType(
                MethodCallDelegateTypeArgs(
                    callSignature,
                    staticCall ? Types.DObject[0] : Types.Object[0],
                    MethodCallDelegateAdditionalArguments(staticCall, methodFullName != null, this.classContextPlace != null),
                    returnType),
                callSitesCount);    // (J) do not create dynamic delegates in dynamic modules, so they can be referenced from non-transient assemblies

            //
            var field = DefineCallSite(cg.IL, string.Format("call_{0}", methodFullName ?? "$"), delegateType, (il) =>
            {
                // <LOAD> Binder.{MethodCall|StaticMethodCall}( methodFullName, genericParamsCount, paramsCount, classContext, <returnType> )
                if (methodFullName != null)
                {
                    il.Emit(OpCodes.Ldstr, methodFullName);
                }
                else
                {
                    il.Emit(OpCodes.Ldnull);
                }
                il.LdcI4(callSignature.GenericParams.Count);
                il.LdcI4(callSignature.Parameters.Count);
                if (this.classContextPlace != null)
                {
                    this.classContextPlace.EmitLoad(il);
                }
                else
                {
                    il.Emit(OpCodes.Ldsfld, Fields.UnknownTypeDesc.Singleton);
                }

                il.Emit(OpCodes.Ldtoken, returnType);
                il.Emit(OpCodes.Call, Methods.GetTypeFromHandle);

                il.Emit(OpCodes.Call, staticCall ? Methods.Binder.StaticMethodCall : Methods.Binder.MethodCall);
            });

            //
            // call the CallSite:
            //

            // <field>.Target( <field>, <targetExpr|self>, <scriptContext>, <callSignature.EmitLoadOnEvalStack>, <targetType>?, (classContext)?, <methodNameExpr>? ):

            cg.IL.Emit(OpCodes.Ldsfld, field);
            cg.IL.Emit(OpCodes.Ldfld, field.FieldType.GetField("Target"));
            cg.IL.Emit(OpCodes.Ldsfld, field);
            if (staticCall)
            {
                cg.EmitLoadSelf();
            }
            else
            {
                EmitMethodTargetExpr(cg, targetExpr);
            }
            cg.EmitLoadScriptContext();
            EmitMethodCallParameters(cg, callSignature);
            if (staticCall)
            {
                targetType.EmitLoadTypeDesc(cg, ResolveTypeFlags.UseAutoload | ResolveTypeFlags.ThrowErrors);
            }
            if (/*!classContextIsKnown*/ this.classContextPlace == null)
            {
                cg.EmitLoadClassContext();
            }
            if (/*!methodNameIsKnown*/ methodFullName == null)
            {
                cg.EmitName(methodFullName /*null*/, methodNameExpr, true);
            }

            cg.MarkTransientSequencePoint();
            cg.IL.Emit(OpCodes.Callvirt, delegateType.GetMethod("Invoke"));

            cg.MarkTransientSequencePoint();

            //
            return(PhpTypeCodeEnum.FromType(returnType));
        }
Ejemplo n.º 7
0
            /// <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>
            public override PhpTypeCode Emit(AssignEx node, CodeGenerator codeGenerator)
            {
                Debug.Assert(access == AccessType.Read || access == AccessType.None || access == AccessType.ReadRef ||
                             access == AccessType.ReadUnknown);
                Statistics.AST.AddNode("Assign.Value");

                AccessType old_selector = codeGenerator.AccessSelector;

                codeGenerator.ChainBuilder.Create();

                PhpTypeCode result;

                if (node.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;
                    node.lvalue.Emit(codeGenerator);
                    codeGenerator.AccessSelector = AccessType.None;

                    PhpTypeCode src_type_code = EmitSourceValRead((ValueAssignEx)node, 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, ((ValueAssignEx)node).rvalue);
                    }
                }
                else
                {
                    // PREPARE a:
                    codeGenerator.AccessSelector = AccessType.Write;
                    node.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((ValueAssignEx)node, codeGenerator);
                    var         rvalue_tmp = codeGenerator.IL.GetTemporaryLocal(PhpTypeCodeEnum.ToType(right_type), false);
                    codeGenerator.IL.Emit(OpCodes.Stloc, rvalue_tmp);
                    EmitDestVarRead(node, codeGenerator);
                    codeGenerator.IL.Emit(OpCodes.Ldloc, rvalue_tmp);
                    codeGenerator.IL.ReturnTemporaryLocal(rvalue_tmp);

                    switch (node.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.AssignPow:
                        codeGenerator.EmitBoxing(right_type);
                        result = codeGenerator.EmitMethodCall(Methods.Operators.Pow.Object_Object);
                        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);
                        codeGenerator.IL.Emit(OpCodes.Ldc_I4, (int)Operators.BitOp.And);
                        result = codeGenerator.EmitMethodCall(Methods.Operators.BitOperation);
                        break;

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

                    case Operations.AssignXor:
                        codeGenerator.EmitBoxing(right_type);
                        codeGenerator.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:
                        throw new InvalidOperationException();
                    }

                    codeGenerator.IL.EmitBoxing(result);
                }

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

                    // STORE tmp
                    codeGenerator.IL.Stloc(codeGenerator.IL.GetAssignmentLocal());

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

                    // LOAD result
                    codeGenerator.IL.Ldloc(codeGenerator.IL.GetAssignmentLocal());

                    result = PhpTypeCode.Object;
                    break;
                }

                case AccessType.ReadRef:
                case AccessType.ReadUnknown:

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

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

                case AccessType.None:

                    // STORE a:
                    codeGenerator.AccessSelector = AccessType.Write;
                    result = VariableUseHelper.EmitAssign(node.LValue, 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);
            }