コード例 #1
0
        /// <summary>
        /// Emits code converting argument on the evaluation stack to a specified type using PHP.NET library conversions.
        /// Used for conversion of elements of params array optional arguments,
        /// for conversion of a content of mandatory by-ref holder, and
        /// for conversion of mandatory in argument.
        /// </summary>
        /// <param name="dstType">
        /// The type of the formal argument. Shouldn't be <see cref="Type.IsByRef"/>.
        /// </param>
        /// <param name="srcTypeOrValue">
        /// The type of the formal argument or its value if it is a literal.
        /// </param>
        /// <param name="allowImplicitCast">Whether to allow implicit cast of types PhpArray, PhpObject, PhpResource.</param>
        /// <param name="param">The formal argument description.</param>
        internal void EmitArgumentConversion(Type dstType, object srcTypeOrValue, bool allowImplicitCast, ParameterInfo param)
        {
            Debug.Assert(!dstType.IsByRef);

            Type src_type = srcTypeOrValue as Type;

            // passing void parameter is the same as passing null reference:
            if (src_type == Types.Void)
            {
                srcTypeOrValue = null;
                src_type       = null;
            }

            // unites treatment of enums and ints:
            if (src_type != null && src_type.IsEnum)
            {
                src_type = typeof(int);
            }
            if (dstType.IsEnum)
            {
                dstType = typeof(int);
            }

            // no conversions needed:
            if (dstType == src_type)
            {
                // deep copy if needed (doesn't produce unnecessary copying):
                if (param.IsDefined(typeof(PhpDeepCopyAttribute), false))
                {
                    // CALL (<dstType>)PhpVariable.Copy(STACK,CopyReason.PassedByCopy);
                    il.LdcI4((int)CopyReason.PassedByCopy);
                    il.Emit(OpCodes.Call, Methods.PhpVariable.Copy);

                    if (dstType != typeof(object))
                    {
                        il.Emit(OpCodes.Castclass, dstType);
                    }
                }
                return;
            }

            // if dst type is reference then src type should be also a reference
            // (reference - reference combination was eliminated in previous statement):
            Debug.Assert(dstType != typeof(PhpReference), "Formal type cannot be reference if actual is not.");

            // dereferences a reference:
            if (src_type == typeof(PhpReference))
            {
                il.Emit(OpCodes.Ldfld, Fields.PhpReference_Value);
                src_type = typeof(object);
            }

            #region dst is integer, long integer, bool, double, string, PhpBytes, char

            // to integer (can be loaded from literal):
            if (dstType == typeof(int))
            {
                if (src_type == null)
                {
                    il.LdcI4(Convert.ObjectToInteger(srcTypeOrValue));
                }
                else
                {
                    // boxing and conversion:
                    if (src_type.IsValueType)
                    {
                        il.Emit(OpCodes.Box, src_type);
                    }
                    il.Emit(OpCodes.Call, Methods.Convert.ObjectToInteger);
                }
                return;
            }

            // to long integer (can be loaded from literal):
            if (dstType == typeof(long))
            {
                if (src_type == null)
                {
                    il.LdcI8(Convert.ObjectToLongInteger(srcTypeOrValue));
                }
                else
                {
                    // boxing and conversion:
                    if (src_type.IsValueType)
                    {
                        il.Emit(OpCodes.Box, src_type);
                    }
                    il.Emit(OpCodes.Call, Methods.Convert.ObjectToLongInteger);
                }
                return;
            }

            // to boolean (can be loaded from literal):
            if (dstType == typeof(bool))
            {
                if (src_type == null)
                {
                    il.LdcI4(Convert.ObjectToBoolean(srcTypeOrValue) ? 1 : 0);
                }
                else
                {
                    // boxing and conversion:
                    if (src_type.IsValueType)
                    {
                        il.Emit(OpCodes.Box, src_type);
                    }
                    il.Emit(OpCodes.Call, Methods.Convert.ObjectToBoolean);
                }
                return;
            }

            // to double (can be loaded from literal):
            if (dstType == typeof(double))
            {
                if (src_type == null)
                {
                    il.Emit(OpCodes.Ldc_R8, Convert.ObjectToDouble(srcTypeOrValue));
                }
                else
                {
                    // boxing and conversion:
                    if (src_type.IsValueType)
                    {
                        il.Emit(OpCodes.Box, src_type);
                    }
                    il.Emit(OpCodes.Call, Methods.Convert.ObjectToDouble);
                }
                return;
            }

            // to string (can be loaded from literal):
            if (dstType == typeof(string))
            {
                if (src_type == null)
                {
                    il.Emit(OpCodes.Ldstr, Convert.ObjectToString(srcTypeOrValue));
                }
                else
                {
                    // boxing and conversion:
                    if (src_type.IsValueType)
                    {
                        il.Emit(OpCodes.Box, src_type);
                    }
                    il.Emit(OpCodes.Call, Methods.Convert.ObjectToString);
                }
                return;
            }

            // to bytes:
            if (dstType == typeof(PhpBytes))
            {
                if (src_type == null)
                {
                    il.EmitLoadPhpBytes(Convert.ObjectToPhpBytes(srcTypeOrValue));
                }
                else
                {
                    // boxing and conversion:
                    if (src_type.IsValueType)
                    {
                        il.Emit(OpCodes.Box, src_type);
                    }
                    il.Emit(OpCodes.Call, Methods.Convert.ObjectToPhpBytes);
                }
                return;
            }

            // to char:
            if (dstType == typeof(char))
            {
                if (src_type == null)
                {
                    il.LdcI4((int)Convert.ObjectToChar(srcTypeOrValue));
                }
                else
                {
                    // boxing and conversion:
                    if (src_type.IsValueType)
                    {
                        il.Emit(OpCodes.Box, src_type);
                    }
                    il.Emit(OpCodes.Call, Methods.Convert.ObjectToChar);
                }
                return;
            }

            #endregion

            // further conversions doesn't work with empty stack => loads literal on eval stack:
            if (src_type == null)
            {
                src_type = il.LoadLiteral(srcTypeOrValue);
            }

            if (src_type.IsValueType)
            {
                il.Emit(OpCodes.Box, src_type);
            }

            // to callback:
            if (dstType == typeof(PhpCallback))
            {
                il.Emit(OpCodes.Call, Methods.Convert.ObjectToCallback);
                return;
            }

            Debug.Assert(!dstType.IsValueType);

            if (param.IsDefined(typeof(PhpDeepCopyAttribute), false))
            {
                // do not copy literals:
                if (!src_type.IsValueType && src_type != typeof(string))
                {
                    // CALL (<src_type>)PhpVariable.Copy(STACK,CopyReason.PassedByCopy);
                    il.LdcI4((int)CopyReason.PassedByCopy);
                    il.Emit(OpCodes.Call, Methods.PhpVariable.Copy);

                    // src_type was on the eval. stack before copy was called:
                    if (src_type != typeof(object))
                    {
                        il.Emit(OpCodes.Castclass, src_type);
                    }
                }
            }

            // to object:
            if (dstType == typeof(object))
            {
                return;
            }

            //
            // cast the value to the target type
            //
            if (allowImplicitCast)
            {
                // cast the value, without the checking of the success
                il.Emit(OpCodes.Isinst, dstType);
            }
            else
            {
                // if implicit cast is not allowed => a condition checking the result of the cast
                // is emitted (conditional call to InvalidImplicitCast)

                // conversion of array, object, and resource:
                string type_name = PhpVariable.GetAssignableTypeName(dstType);

                Label endif_label = il.DefineLabel();

                LocalBuilder loc_typed = null;
                LocalBuilder loc_obj   = il.DeclareLocal(typeof(object));
                il.Emit(OpCodes.Dup);
                il.Stloc(loc_obj);

                // IF (obj == null) goto ENDIF;
                il.Emit(OpCodes.Ldnull);
                il.Emit(OpCodes.Beq_S, endif_label);

                //
                il.Ldloc(loc_obj);
                // (obj) on top of eval stack, eat it:

                if (dstType.IsSealed)
                {
                    // if (<obj>.GetType() == typeof(<dstType>)) goto ENDIF;    // little JIT hack
                    il.Emit(OpCodes.Callvirt, Methods.Object_GetType);
                    il.Emit(OpCodes.Ldtoken, dstType);
                    il.Emit(OpCodes.Call, Methods.GetTypeFromHandle);
                    il.Emit(OpCodes.Call, Methods.Equality_Type_Type);
                    il.Emit(OpCodes.Brtrue, endif_label);
                }
                else
                {
                    loc_typed = il.DeclareLocal(dstType);

                    // <loc_typed> = <obj> as <dstType>:
                    il.Emit(OpCodes.Isinst, dstType);
                    il.Emit(OpCodes.Dup);
                    il.Stloc(loc_typed);

                    // (obj as dstType) is on top of the evaluation stack

                    // IF (obj!=null) goto ENDIF;
                    il.Emit(OpCodes.Brtrue_S, endif_label);
                }

                if (true)
                {
                    // pops all arguments already pushed:
                    for (int i = 0; i < pushedArgsCount; ++i)
                    {
                        il.Emit(OpCodes.Pop);
                    }

                    // CALL PhpException.InvalidImplicitCast(obj,<PhpTypeName>,<functionName>);
                    il.Ldloc(loc_obj);
                    il.Emit(OpCodes.Ldstr, type_name);
                    il.Emit(OpCodes.Ldstr, this.functionName.ToString());
                    il.Emit(OpCodes.Call, Methods.PhpException.InvalidImplicitCast);

                    if (debug)
                    {
                        il.Emit(OpCodes.Nop);
                    }

                    Debug.Assert(overloadCallSkipEmitter != null);

                    // GOTO <end of call>;
                    //il.Emit(OpCodes.Br, overloadCallEndLabel);
                    overloadCallSkipEmitter(il);
                }

                // ENDIF;
                il.MarkLabel(endif_label);

                // load typed <obj> (already casted in <loc_typed> or boxed in <loc_obj>
                if (loc_typed != null)
                {
                    il.Ldloc(loc_typed);
                }
                else
                {
                    il.Ldloc(loc_obj);
                    il.Emit(OpCodes.Castclass, dstType);
                }
            }
        }