/// <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); } }
public void EmitConvertReturnValue(Type /*!*/ returnType, PhpTypeCode expectedTypeCode) { if (returnType == Types.Void) { il.Emit(OpCodes.Pop); } else { if (expectedTypeCode == PhpTypeCode.PhpReference) { // dereference il.Emit(OpCodes.Ldfld, Fields.PhpReference_Value); expectedTypeCode = PhpTypeCode.Object; } ClrOverloadBuilder.EmitConvertToClr( il, expectedTypeCode, returnType); } }
/// <summary> /// Emits code that stores a <see cref="PhpReference"/>'s value back to a ref/out parameter. /// </summary> /// <param name="paramInfo">The parameter to store back.</param> public void EmitStoreClrParameter(ParameterInfo /*!*/ paramInfo) { if (paramInfo.ParameterType.IsByRef && referenceLocals[paramInfo.Position] != null) { il.Ldarg(paramInfo.Position + paramOffset); Type param_type = paramInfo.ParameterType.GetElementType(); // load the new parameter value il.Ldloc(referenceLocals[paramInfo.Position]); il.Emit(OpCodes.Ldfld, Fields.PhpReference_Value); // convert it to CLR type ClrOverloadBuilder.EmitConvertToClr( il, PhpTypeCode.Object, param_type); // store it back il.Stind(param_type); } }
/// <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<T> /// return new DuckEnumerableWrapper<T>(obj.GetForeachEnumerator(false, false, null)) /// /// // type is IDuckKeyedEnumerable<T> /// return new DuckKeyedEnumerableWrapper<T>(obj.GetForeachEnumerator(true, false, null)) /// /// // type is marked using [DuckType] /// return DuckTyping.Instance.ImplementDuckType<T>(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); } } }