/// <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); } } }