private static void GenerateInvokeInputCode(ILGenerator ilGenerator, NativeIlGenContext context, LocalBuilder argsLocal) { foreach (var parameter in context.Parameters) { if (parameter.Property != null) { // args[i] = this.IdentifierI; ilGenerator.Emit(OpCodes.Ldloc, argsLocal); ilGenerator.Emit(OpCodes.Ldc_I4, parameter.Index); ilGenerator.Emit(OpCodes.Ldarg_0); ilGenerator.EmitCall(/*Callvirt*/ parameter.Property.GetGetMethod(true)); ilGenerator.Emit(OpCodes.Box, typeof(int)); ilGenerator.Emit(OpCodes.Stelem_Ref); } else { // If this parameter is of an output type no pass-trough is required; skip it. if (parameter.Parameter.IsOut) { continue; } // args[i] = argI; ilGenerator.Emit(OpCodes.Ldloc, argsLocal); ilGenerator.Emit(OpCodes.Ldc_I4, parameter.Index); ilGenerator.Emit(OpCodes.Ldarg, parameter.Parameter.Position + 1); if (parameter.Parameter.ParameterType.IsValueType) { ilGenerator.Emit(OpCodes.Box, parameter.Parameter.ParameterType); } ilGenerator.Emit(OpCodes.Stelem_Ref); } } }
private static MethodInfo GetHandleInvokerMethod(NativeIlGenContext context) { // Pick the right invoke method based on the return type of the delegate. MethodInfo result; const BindingFlags flags = BindingFlags.Public | BindingFlags.Static; if (context.BaseMethod.ReturnType == typeof(int)) { result = typeof(NativeHandleInvokers).GetMethod(nameof(NativeHandleInvokers.InvokeHandle), flags); } else if (context.BaseMethod.ReturnType == typeof(bool)) { result = typeof(NativeHandleInvokers).GetMethod(nameof(NativeHandleInvokers.InvokeHandleAsBool), flags); } else if (context.BaseMethod.ReturnType == typeof(float)) { result = typeof(NativeHandleInvokers).GetMethod(nameof(NativeHandleInvokers.InvokeHandleAsFloat), flags); } else if (context.BaseMethod.ReturnType == typeof(void)) { result = typeof(NativeHandleInvokers).GetMethod(nameof(NativeHandleInvokers.InvokeHandleAsVoid), flags); } else { throw new Exception("Unsupported return type of method"); } if (result == null) { throw new Exception("Native invoker is missing"); } return(result); }
private static void Generate(ILGenerator ilGenerator, IntPtr native, NativeIlGenContext context) { // Will be using direct reference to loc_0 for optimized calls to data buffer var data = ilGenerator.DeclareLocal(typeof(int *)); if (data.LocalIndex != 0) { throw new Exception("data should be local 0"); } // Data buffer will contain all param references + value type values var stackSize = context.Parameters.Length + context.Parameters.Count(param => (param.Type & NativeParameterType.ValueTypeMask) != 0); // Must provide an arguments pointer to invoke_native, even if no arguments are required. if (stackSize == 0) { stackSize = 1; } // int* data = stackalloc int[n]; ilGenerator.Emit(OpCodes.Ldc_I4, stackSize * 4); //4 bytes per cell ilGenerator.Emit(OpCodes.Conv_U); ilGenerator.Emit(OpCodes.Localloc); ilGenerator.Emit(OpCodes.Stloc_0); EmitInParamAssignment(ilGenerator, context, out var paramBuffers); EmitInvokeNative(ilGenerator, native, context); EmitOutParamAssignment(ilGenerator, context, paramBuffers); EmitReturnCast(ilGenerator, context); // return $0 ilGenerator.Emit(OpCodes.Ret); }
private static LocalBuilder GenerateArgsArray(ILGenerator ilGenerator, NativeIlGenContext context) { // args = new object[params.Length] var result = ilGenerator.DeclareLocal(typeof(object[])); ilGenerator.Emit(OpCodes.Ldc_I4_S, context.Parameters.Length); ilGenerator.Emit(OpCodes.Newarr, typeof(object)); ilGenerator.Emit(OpCodes.Stloc, result); return(result); }
private void Generate(ILGenerator ilGenerator, NativeIlGenContext context, INative native) { var argsLocal = GenerateArgsArray(ilGenerator, context); GenerateInvokeInputCode(ilGenerator, context, argsLocal); GenerateHandleInvokeCode(ilGenerator, native, context, argsLocal); GenerateInvokeOutputCode(ilGenerator, context, argsLocal); // return $0 ilGenerator.Emit(OpCodes.Ret); }
private static string GenerateCallFormat(NativeIlGenContext context) { // context. ReferenceIndices var formatStringBuilder = new StringBuilder(); foreach (var param in context.Parameters) { switch (param.Type) { case NativeParameterType.Int32: case NativeParameterType.Single: case NativeParameterType.Bool: formatStringBuilder.Append(param.IsReferenceInput ? "r" : "d"); break; case NativeParameterType.Int32Reference: case NativeParameterType.SingleReference: case NativeParameterType.BoolReference: formatStringBuilder.Append("R"); break; case NativeParameterType.String: formatStringBuilder.Append("s"); break; case NativeParameterType.StringReference: formatStringBuilder.Append($"S[*{param.LengthParam.Index}]"); break; case NativeParameterType.Int32Array: case NativeParameterType.SingleArray: case NativeParameterType.BoolArray: formatStringBuilder.Append($"a[*{param.LengthParam.Index}]"); break; case NativeParameterType.Int32ArrayReference: case NativeParameterType.SingleArrayReference: case NativeParameterType.BoolArrayReference: formatStringBuilder.Append($"A[*{param.LengthParam.Index}]"); break; default: throw new Exception("Unknown parameter type"); } } return(formatStringBuilder.ToString()); }
private static void GenerateInvokeOutputCode(ILGenerator ilGenerator, NativeIlGenContext context, LocalBuilder argsLocal) { // Generate a pass-back for every output parameter of the native. foreach (var parameter in context.Parameters) { if (!parameter.Type.HasFlag(NativeParameterType.Reference)) { continue; } // argI = args[i]; ilGenerator.Emit(OpCodes.Ldarg, parameter.Parameter.Position + 1); ilGenerator.Emit(OpCodes.Ldloc, argsLocal); ilGenerator.Emit(OpCodes.Ldc_I4, parameter.Index); ilGenerator.Emit(OpCodes.Ldelem_Ref); var elType = parameter.InputType.GetElementType(); if (elType == typeof(int)) { ilGenerator.Emit(OpCodes.Unbox_Any, typeof(int)); ilGenerator.Emit(OpCodes.Stind_I4); } else if (elType == typeof(bool)) { ilGenerator.Emit(OpCodes.Unbox_Any, typeof(bool)); ilGenerator.Emit(OpCodes.Stind_I4); } else if (elType == typeof(float)) { ilGenerator.Emit(OpCodes.Unbox_Any, typeof(float)); ilGenerator.Emit(OpCodes.Stind_R4); } else { ilGenerator.Emit(OpCodes.Stind_Ref); } } }
private static void EmitInvokeNative(ILGenerator ilGenerator, IntPtr native, NativeIlGenContext context) { var formatString = GenerateCallFormat(context); // if (_synchronizationProvider.InvokeRequired) ilGenerator.Emit(OpCodes.Ldarg_0); ilGenerator.Emit(OpCodes.Ldfld, context.ProxyGeneratedFields[0]); ilGenerator.EmitPropertyGetterCall <ISynchronizationProvider>(OpCodes.Callvirt, x => x.InvokeRequired); var elseLabel = ilGenerator.DefineLabel(); var endifLabel = ilGenerator.DefineLabel(); ilGenerator.Emit(OpCodes.Brfalse, elseLabel); // NativeUtils.SynchronizeInvoke(_synchronizationProvider, new IntPtr($0), format, ptr); ilGenerator.Emit(OpCodes.Ldarg_0); ilGenerator.Emit(OpCodes.Ldfld, context.ProxyGeneratedFields[0]); ilGenerator.Emit(OpCodes.Ldc_I4, (int)native); ilGenerator.Emit(OpCodes.Newobj, typeof(IntPtr).GetConstructor(new[] { typeof(int) })); ilGenerator.Emit(OpCodes.Ldstr, formatString); ilGenerator.Emit(OpCodes.Ldloc_0); ilGenerator.EmitCall(typeof(NativeUtils), nameof(NativeUtils.SynchronizeInvoke)); // else ilGenerator.Emit(OpCodes.Br, endifLabel); ilGenerator.MarkLabel(elseLabel); // SampSharp.Core.Hosting.Interop.FastNativeInvoke(new IntPtr($0), format, ptr); ilGenerator.Emit(OpCodes.Ldc_I4, (int)native); ilGenerator.Emit(OpCodes.Newobj, typeof(IntPtr).GetConstructor(new[] { typeof(int) })); ilGenerator.Emit(OpCodes.Ldstr, formatString); ilGenerator.Emit(OpCodes.Ldloc_0); ilGenerator.EmitCall(typeof(Interop), nameof(Interop.FastNativeInvoke)); // endif ilGenerator.Emit(OpCodes.Br, endifLabel); ilGenerator.MarkLabel(endifLabel); }
private static void EmitReturnCast(ILGenerator ilGenerator, NativeIlGenContext context) { if (context.BaseMethod.ReturnType == typeof(void)) { ilGenerator.Emit(OpCodes.Pop); } else if (context.BaseMethod.ReturnType == typeof(bool)) { ilGenerator.EmitConvert <int, bool>(); } else if (context.BaseMethod.ReturnType == typeof(float)) { ilGenerator.EmitConvert <int, float>(); } else if (context.BaseMethod.ReturnType == typeof(int)) { // nop } else { throw new Exception("Unsupported return type of native"); } }
protected override MethodBuilder CreateMethodBuilder(TypeBuilder typeBuilder, NativeIlGenContext context) { // Find the native. var sizes = context.Parameters .Select(p => p.LengthParam) .Where(p => p != null) .Select(p => (uint)p.Index) .ToArray(); var types = context.Parameters .Select(p => p.InputType) .ToArray(); var native = _nativeLoader.Load(context.NativeName, sizes, types); if (native == null) { return(null); } // Generate the method body. var methodBuilder = typeBuilder.DefineMethod(context.BaseMethod.Name, context.MethodOverrideAttributes, context.BaseMethod.ReturnType, context.MethodParameterTypes); Generate(methodBuilder.GetILGenerator(), context, native); return(methodBuilder); }
private static void GenerateHandleInvokeCode(ILGenerator ilGenerator, INative native, NativeIlGenContext context, LocalBuilder argsLocal) { // NativeHandleInvokers.InvokeHandle(handle, args) ilGenerator.Emit(OpCodes.Ldc_I4, native.Handle); ilGenerator.Emit(OpCodes.Ldloc, argsLocal); ilGenerator.EmitCall(GetHandleInvokerMethod(context)); }
/// <inheritdoc /> protected override MethodBuilder CreateMethodBuilder(TypeBuilder typeBuilder, NativeIlGenContext context) { // Find the native. var native = Interop.FastNativeFind(context.NativeName); if (native == IntPtr.Zero) { return(null); } var methodBuilder = typeBuilder.DefineMethod(context.BaseMethod.Name, context.MethodOverrideAttributes, context.BaseMethod.ReturnType, context.MethodParameterTypes); var ilGenerator = methodBuilder.GetILGenerator(); Generate(ilGenerator, native, context); return(methodBuilder); }
private static void EmitOutParamAssignment(ILGenerator ilGenerator, NativeIlGenContext context, LocalBuilder[] paramBuffers) { var dataIndex = context.Parameters.Length; for (var i = 0; i < context.Parameters.Length; i++) { var param = context.Parameters[i]; if ((param.Type & NativeParameterType.ValueTypeMask) != 0 && !param.Type.HasFlag(NativeParameterType.Array)) { if (param.Type.HasFlag(NativeParameterType.Reference)) { ilGenerator.Emit(OpCodes.Ldarg, param.Parameter); ilGenerator.Emit(OpCodes.Ldloc_0); ilGenerator.Emit(OpCodes.Ldc_I4, dataIndex * 4); ilGenerator.Emit(OpCodes.Add); ilGenerator.Emit(OpCodes.Ldind_I4); switch (param.Type) { case NativeParameterType.SingleReference: ilGenerator.EmitConvert <int, float>(); ilGenerator.Emit(OpCodes.Stind_R4); break; case NativeParameterType.Int32Reference: ilGenerator.Emit(OpCodes.Stind_I4); break; case NativeParameterType.BoolReference: ilGenerator.EmitConvert <int, bool>(); ilGenerator.Emit(OpCodes.Stind_I1); break; default: throw new Exception("Unknown native parameter type"); } } dataIndex++; } else if (param.Type.HasFlag(NativeParameterType.Array | NativeParameterType.Reference)) { // argI = NativeUtils.IntSpanToArray<int>((Array)null, span); ilGenerator.Emit(OpCodes.Ldarg, param.Parameter); if (param.Parameter.IsOut) { ilGenerator.Emit(OpCodes.Ldnull); } else { ilGenerator.Emit(OpCodes.Ldarg, param.Parameter); } ilGenerator.Emit(OpCodes.Ldloc, paramBuffers[i]); var method = typeof(NativeUtils).GetMethod(nameof(NativeUtils.IntSpanToArray)) .MakeGenericMethod(param.InputType.GetElementType().GetElementType()); ilGenerator.EmitCall(method); ilGenerator.Emit(OpCodes.Stind_Ref); } else if (param.Type == NativeParameterType.String || param.Type.HasFlag(NativeParameterType.Array)) { // uses own buffer } else if (param.Type == NativeParameterType.StringReference) { // paramStr = NativeUtils.GetString(strBuf); ilGenerator.Emit(OpCodes.Ldarg, param.Parameter); ilGenerator.Emit(OpCodes.Ldloc, paramBuffers[i]); ilGenerator.EmitCall(OpCodes.Call, typeof(NativeUtils).GetMethod(nameof(NativeUtils.GetString)), null); ilGenerator.Emit(OpCodes.Stind_Ref); } else { throw new Exception("Unknown native parameter type"); } } }
private static void EmitInParamAssignment(ILGenerator ilGenerator, NativeIlGenContext context, out LocalBuilder[] paramBuffers) { paramBuffers = new LocalBuilder[context.Parameters.Length]; var dataIndex = context.Parameters.Length; for (var i = 0; i < context.Parameters.Length; i++) { void EmitBufferLocation() { ilGenerator.Emit(OpCodes.Ldloc_0); if (i > 0) { ilGenerator.Emit(OpCodes.Ldc_I4, i * 4); ilGenerator.Emit(OpCodes.Add); } } var param = context.Parameters[i]; if ((param.Type & NativeParameterType.ValueTypeMask) != 0 && !param.Type.HasFlag(NativeParameterType.Array)) // in/out int/float { // data[i] = NativeUtils.IntPointerToInt(ptr + dataIndex); EmitBufferLocation(); ilGenerator.Emit(OpCodes.Ldloc_0); ilGenerator.Emit(OpCodes.Ldc_I4, dataIndex * 4); // + 1 (* 4 bytes) ilGenerator.Emit(OpCodes.Add); ilGenerator.EmitCall(OpCodes.Call, typeof(NativeUtils), nameof(NativeUtils.IntPointerToInt)); ilGenerator.Emit(OpCodes.Stind_I4); if (param.Property != null) { // data[dataIndex] = Property; ilGenerator.Emit(OpCodes.Ldloc_0); ilGenerator.Emit(OpCodes.Ldc_I4, dataIndex * 4); ilGenerator.Emit(OpCodes.Add); ilGenerator.Emit(OpCodes.Ldarg_0); ilGenerator.EmitCall(param.Property.GetMethod); EmitConvertToInt(ilGenerator, param.Type); ilGenerator.Emit(OpCodes.Stind_I4); } else if (!param.Parameter.IsOut) { // data[dataIndex] = argI ilGenerator.Emit(OpCodes.Ldloc_0); ilGenerator.Emit(OpCodes.Ldc_I4, dataIndex * 4); ilGenerator.Emit(OpCodes.Add); ilGenerator.Emit(OpCodes.Ldarg, param.Parameter); // arg0=this, arg1=1st arg EmitConvertToInt(ilGenerator, param.Type); ilGenerator.Emit(OpCodes.Stind_I4); } dataIndex++; } else if (param.Type == NativeParameterType.String) // in string { if (param.Property != null) { throw new Exception("Unsupported identifier property type"); } var strLen = ilGenerator.DeclareLocal(typeof(int)); // int byteCount = NativeUtils.GetByteCount(textString); ilGenerator.Emit(OpCodes.Ldarg, param.Parameter); ilGenerator.EmitCall(typeof(NativeUtils), nameof(NativeUtils.GetByteCount)); ilGenerator.Emit(OpCodes.Stloc, strLen); // Span<byte> strBuffer = stackalloc/new byte[...] var strBufferSpan = EmitSpanAlloc(ilGenerator, strLen); // NativeUtils.GetBytes(textString, strBuffer); ilGenerator.Emit(OpCodes.Ldarg, param.Parameter); ilGenerator.Emit(OpCodes.Ldloc, strBufferSpan); ilGenerator.EmitCall(typeof(NativeUtils), nameof(NativeUtils.GetBytes)); // data[i] = NativeUtils.BytePointerToInt(strBuffer); EmitBufferLocation(); // data[i] EmitByteSpanToPointer(ilGenerator, strBufferSpan); ilGenerator.EmitCall(typeof(NativeUtils), nameof(NativeUtils.BytePointerToInt)); ilGenerator.Emit(OpCodes.Stind_I4); } else if (param.Type == NativeParameterType.StringReference) { EmitThrowOnOutOfRangeLength(ilGenerator, param.LengthParam); // var strBuf = stackalloc/new byte[...] var strBuf = EmitSpanAlloc(ilGenerator, param.LengthParam.Parameter); paramBuffers[i] = strBuf; // data[i] = NativeUtils.BytePointerToInt(strBufPtr); EmitBufferLocation(); // data[i] EmitByteSpanToPointer(ilGenerator, strBuf); ilGenerator.EmitCall(typeof(NativeUtils), nameof(NativeUtils.BytePointerToInt)); ilGenerator.Emit(OpCodes.Stind_I4); } else if (param.Type.HasFlag(NativeParameterType.Array)) { if (param.Property != null) { throw new Exception("Unsupported identifier property type"); } EmitThrowOnOutOfRangeLength(ilGenerator, param.LengthParam); // var arraySpan = NativeUtils.ArrayToIntSpan(array/null,len) var arraySpan = ilGenerator.DeclareLocal(typeof(Span <int>)); if (param.Parameter.IsOut) { ilGenerator.Emit(OpCodes.Ldnull); } else { ilGenerator.Emit(OpCodes.Ldarg, param.Parameter); } ilGenerator.Emit(OpCodes.Ldarg, param.LengthParam.Parameter); ilGenerator.EmitCall(typeof(NativeUtils), nameof(NativeUtils.ArrayToIntSpan)); ilGenerator.Emit(OpCodes.Stloc, arraySpan); // data[i] = NativeUtils.BytePointerToInt(strBufPtr); EmitBufferLocation(); // data[i] EmitIntSpanToPointer(ilGenerator, arraySpan); ilGenerator.EmitCall(typeof(NativeUtils), nameof(NativeUtils.IntPointerToInt)); ilGenerator.Emit(OpCodes.Stind_I4); if (param.Type.HasFlag(NativeParameterType.Reference)) { paramBuffers[i] = arraySpan; } } else { throw new Exception("Unknown native parameter type"); } } }