Beispiel #1
0
        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);
                }
            }
        }
Beispiel #2
0
        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);
        }
Beispiel #4
0
        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);
        }
Beispiel #5
0
        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());
        }
Beispiel #7
0
        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");
     }
 }
Beispiel #10
0
        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);
        }
Beispiel #11
0
 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");
                }
            }
        }