void iMethodPrefab.emitMethod(MethodBuilder mb, FieldBuilder field, CustomConventionsAttribute customConventions) { ILGenerator il = mb.GetILGenerator(); var prologue = customConventions?.prologue; if (null != prologue) { il.EmitCall(OpCodes.Call, prologue, null); } // Load the delegate from this il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, field); // Load arguments for (int i = 0; i < method.GetParameters().Length; i++) { il.loadArg(i + 1); } // Call the delegate MethodInfo invoke = field.FieldType.GetMethod("Invoke"); il.Emit(OpCodes.Callvirt, invoke); il.Emit(OpCodes.Ret); }
void iMethodPrefab.emitMethod(MethodBuilder mb, FieldBuilder field, CustomConventionsAttribute customConventions) { ParameterInfo[] parameters = method.GetParameters(); // Method body ILGenerator il = mb.GetILGenerator(); var prologue = customConventions?.prologue; if (null != prologue) { il.EmitCall(OpCodes.Call, prologue, null); } // Load the delegate from this il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, field); // Load nativePointer from the base class il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, fiNativePointer); // Load arguments for (int i = 0; i < parameters.Length; i++) { il.loadArg(i + 1); } // Call the delegate MethodInfo invoke = field.FieldType.GetMethod("Invoke"); il.Emit(OpCodes.Callvirt, invoke); if (method.ReturnType == typeof(void)) { MethodInfo mi = customConventions?.throwException ?? miThrow; // Call ErrorCodes.throwForHR il.EmitCall(OpCodes.Call, mi, null); } else if (method.ReturnType == typeof(bool)) { MethodInfo mi = customConventions?.throwAndReturnBool ?? miThrowRetBool; il.EmitCall(OpCodes.Call, mi, null); } il.Emit(OpCodes.Ret); }
/// <summary>Build the prefab, it contains 2 expressions that need late binding, `tNativeDelegate nativeDelegate`, and `IntPtr nativeComPointer`, represented by paramNativeObject constant object.</summary> public CustomMarshallerMethod(MethodInfo mi, Type tNativeDelegate, int idx, CustomConventionsAttribute customConventions) { method = mi; methodIndex = idx; this.tNativeDelegate = tNativeDelegate; eNativeDelegate = Expression.Parameter(tNativeDelegate, "nativeDelegate"); ParameterInfo[] parameters = mi.GetParameters(); managedParameters = parameters.Select(pi => Expression.Parameter(pi.ParameterType, pi.Name)).ToArray(); // tDelegate = Expression.GetDelegateType( parameters.Select( pi => pi.ParameterType ).Concat( new Type[ 1 ] { mi.ReturnType } ).ToArray() ); // Expression.GetDelegateType doesn't work on desktop .NET, crashes later saying "Unable to make a reference to a transient module from a non-transient module." // No big deal, creating them manually with emitManagedDelegate so they're in the same assembly. List <ParameterExpression> localVars = new List <ParameterExpression>(); int nativeParamsCount = parameters.Length + 1; int retValIndex = -1; ParameterExpression nativeRetVal = null; if (mi.GetCustomAttribute <RetValIndexAttribute>() is RetValIndexAttribute rvi) { nativeParamsCount++; retValIndex = rvi.index; if (mi.ReturnType.IsInterface) { nativeRetVal = Expression.Parameter(typeof(IntPtr), "retValObj"); } else { nativeRetVal = Expression.Parameter(mi.ReturnType, "retVal"); } localVars.Add(nativeRetVal); } Expression[] nativeParameters = new Expression[nativeParamsCount]; nativeParameters[0] = paramNativeObject; int iNative = 1; List <Expression> after = new List <Expression>(); for (int i = 0; i < parameters.Length; i++, iNative++) { if (i == retValIndex) { nativeParameters[iNative] = nativeRetVal; retValIndex = -1; i--; continue; } var pi = parameters[i]; var cm = pi.customMarshaller(); if (null == cm) { nativeParameters[iNative] = managedParameters[i]; continue; } var customExpressions = cm.native(managedParameters[i], !pi.IsOut); nativeParameters[iNative] = customExpressions.argument; if (null != customExpressions.variable) { localVars.Add(customExpressions.variable); } if (null != customExpressions.after) { after.Add(customExpressions.after); } } if (retValIndex >= 0) { // User has specified [RetValIndex] value after the rest of the parameters nativeParameters[iNative] = nativeRetVal; } List <Expression> block = new List <Expression>(); MethodInfo miInvokeNative = eNativeDelegate.Type.GetMethod("Invoke"); Expression eCall = Expression.Call(eNativeDelegate, miInvokeNative, nativeParameters); List <Expression> blockEntries = new List <Expression>(); if (null != nativeRetVal) { // int hr = nativeCall( ... ) ParameterExpression hr = Expression.Variable(typeof(int), "hr"); localVars.Add(hr); block.Add(Expression.Assign(hr, eCall)); // Append after expressions, if any block.AddRange(after); // ErrorCodes.throwForHR( hr ) MethodInfo mit = customConventions?.throwException ?? miThrow; block.Add(Expression.Call(mit, hr)); LabelTarget returnTarget = Expression.Label(method.ReturnType); if (mi.ReturnType.IsInterface) { // return NativeWrapper.wrap<mi.ReturnType>( nativeRetVal ) MethodInfo miWrapNative = typeof(NativeWrapper) .GetMethod("wrap", new Type[1] { typeof(IntPtr) }) .MakeGenericMethod(mi.ReturnType); block.Add(Expression.Return(returnTarget, Expression.Call(miWrapNative, nativeRetVal))); } else { // return nativeRetVal block.Add(Expression.Return(returnTarget, nativeRetVal)); } block.Add(Expression.Label(returnTarget, Expression.Default(method.ReturnType))); } else if (mi.ReturnType == typeof(void)) { MethodInfo mit = customConventions?.throwException ?? miThrow; block.Add(Expression.Call(mit, eCall)); block.AddRange(after); } else if (mi.ReturnType == typeof(int)) { if (localVars.Count <= 0 && after.Count <= 0) { methodExpression = eCall; return; } ParameterExpression hr = Expression.Variable(typeof(int), "hr"); localVars.Add(hr); block.Add(Expression.Assign(hr, eCall)); block.AddRange(after); LabelTarget returnTarget = Expression.Label(typeof(int)); block.Add(Expression.Return(returnTarget, hr)); block.Add(Expression.Label(returnTarget, Expression.Constant(IUnknown.E_UNEXPECTED))); } else if (mi.ReturnType == typeof(IntPtr)) { if (localVars.Count <= 0 && after.Count <= 0) { methodExpression = eCall; return; } ParameterExpression resultVar = Expression.Variable(typeof(IntPtr), "resultPtr"); localVars.Add(resultVar); block.Add(Expression.Assign(resultVar, eCall)); block.AddRange(after); LabelTarget returnTarget = Expression.Label(typeof(IntPtr)); block.Add(Expression.Return(returnTarget, resultVar)); block.Add(Expression.Label(returnTarget, Expression.Constant(IntPtr.Zero))); } else if (mi.ReturnType == typeof(bool)) { ParameterExpression resultVar = Expression.Variable(typeof(int), "hr"); localVars.Add(resultVar); block.Add(Expression.Assign(resultVar, eCall)); block.AddRange(after); LabelTarget returnTarget = Expression.Label(typeof(bool)); var mit = customConventions?.throwAndReturnBool ?? miThrowRetBool; block.Add(Expression.Return(returnTarget, Expression.Call(mit, resultVar))); block.Add(Expression.Label(returnTarget, MiscUtils.eFalse)); } else { throw new ArgumentException($"Unsupported return type { mi.ReturnType.FullName }, must be int, void, bool or pointer"); } methodExpression = Expression.Block(localVars, block); }