/// <summary> /// Emits a call that obtains the hash code for the current service instance. /// </summary> /// <param name="il">The <see cref="ILProcessor"/> that points to the method body.</param> /// <param name="module">The target module.</param> /// <param name="serviceInstance">The local variable that contains the service instance.</param> private void GetServiceHash(ILProcessor il, ModuleDefinition module, VariableDefinition serviceInstance) { il.Emit(OpCodes.Ldloc, serviceInstance); var getHashCodeMethod = module.ImportMethod<object>("GetHashCode"); il.Emit(OpCodes.Callvirt, getHashCodeMethod); }
private static void TestSequence1(ILProcessor il) { TypeReference type = new TypeReference("", "Test", null, null); FieldDefinition blank = new FieldDefinition("Test", FieldAttributes.Public, type); il.Emit(OpCodes.Nop); il.Emit(OpCodes.Ldsfld, blank); il.Emit(OpCodes.Stsfld, blank); il.Emit(OpCodes.Ret); }
/// <summary> /// Emit a reference to an arbitrary object. Note that the references "leak." /// </summary> public static int EmitGetReference <T>(this CIL.ILProcessor il, int id) { Type t = typeof(T); il.Emit(CIL.OpCodes.Ldc_I4, id); il.Emit(CIL.OpCodes.Call, _GetReference); if (t.GetTypeInfo().IsValueType) { il.Emit(CIL.OpCodes.Unbox_Any, t); } return(id); }
protected override void ImplementProceed(MethodDefinition methodInfo, MethodBody methodBody, ILProcessor il, FieldReference methodInfoField, MethodReference proceed, Action<ILProcessor> emitProceedTarget, MethodReference proceedTargetMethod, OpCode proceedOpCode) { // If T is an interface, then we want to check if target is null; if so, we want to just return the default value var targetNotNull = il.Create(OpCodes.Nop); EmitProxyFromProceed(il); il.Emit(OpCodes.Ldfld, ClassWeaver.Target); // Load "target" from "this" il.Emit(OpCodes.Brtrue, targetNotNull); // If target is not null, jump below CecilExtensions.CreateDefaultMethodImplementation(methodBody.Method, il); il.Append(targetNotNull); // Mark where the previous branch instruction should jump to base.ImplementProceed(methodInfo, methodBody, il, methodInfoField, proceed, emitProceedTarget, proceedTargetMethod, proceedOpCode); }
/// <summary> /// Emit a reference to an arbitrary object. Note that the references "leak." /// </summary> public static int EmitGetReference <T>(this CIL.ILProcessor il, int id) { ModuleDefinition ilModule = il.Body.Method.Module; Type t = typeof(T); il.Emit(CIL.OpCodes.Ldc_I4, id); il.Emit(CIL.OpCodes.Call, ilModule.ImportReference(_GetReference)); if (t.IsValueType) { il.Emit(CIL.OpCodes.Unbox_Any, ilModule.ImportReference(t)); } return(id); }
public static void CompileTarget(ILProcessor processor, IAstExpression target, IAstMethodReference function, CilCompilationContext context) { context.Compile(target); if (function.Location == MethodLocation.Extension) return; var targetType = context.ConvertReference(target.ExpressionType); if (!targetType.IsValueType) return; var variable = context.DefineVariable("x", targetType); processor.Emit(OpCodes.Stloc, variable); processor.Emit(OpCodes.Ldloca_S, variable); }
private void AddFieldConstructorArgument(MethodDefinition constructor, FieldDefinition field, int argumentId, ILProcessor processor) { constructor.Parameters.Add(new ParameterDefinition(field.Name, ParameterAttributes.None, field.FieldType)); processor.Emit(OpCodes.Ldarg_0); if (argumentId == 1) { processor.Emit(OpCodes.Ldarg_1); } else if (argumentId == 2) { processor.Emit(OpCodes.Ldarg_2); } else if (argumentId == 3) { processor.Emit(OpCodes.Ldarg_3); } else { processor.Emit(OpCodes.Ldarg, argumentId); } processor.Emit(OpCodes.Stfld, field); }
public override void EmitAddress(ILProcessor ilProcessor) { if (Previous.Type.Resolve().IsValueType) Previous.EmitAddress(ilProcessor); else Previous.Emit(ilProcessor); ilProcessor.Emit(OpCodes.Ldflda, Field); }
private static void HandleInterceptReturnValue(MethodDefinition method, ILProcessor processor) { if (method.ReturnType.IsValueType) { // unbox processor.Emit(OpCodes.Unbox_Any, method.ReturnType); } else if (method.ReturnType == WeavingInformation.ModuleDefinition.TypeSystem.Void) { // remove return value of intercept method from stack processor.Emit(OpCodes.Pop); } else { // cast to reference type processor.Emit(OpCodes.Castclass, method.ReturnType); } }
/// <summary> /// Emits the instructions that will obtain the <see cref="IMicroContainer"/> instance. /// </summary> /// <param name="module">The target module.</param> /// <param name="microContainerType">The type reference that points to the <see cref="IMicroContainer"/> type.</param> /// <param name="worker">The <see cref="CilWorker"/> that points to the <see cref="IMicroContainer.GetInstance"/> method body.</param> /// <param name="skipCreate">The skip label that will be used if the service cannot be instantiated.</param> protected override void EmitGetContainerInstance(ModuleDefinition module, TypeReference microContainerType, ILProcessor il, Instruction skipCreate) { var getNextContainer = module.ImportMethod<IMicroContainer>("get_NextContainer"); EmitGetNextContainerCall(il, microContainerType, getNextContainer); il.Emit(OpCodes.Brfalse, skipCreate); // var result = NextContainer.GeService(serviceType, serviceName); EmitGetNextContainerCall(il, microContainerType, getNextContainer); }
/// <summary> /// Fill the DynamicMethod with a stub. /// </summary> public static DynamicMethodDefinition Stub(this DynamicMethodDefinition dmd) { CIL.ILProcessor il = dmd.GetILProcessor(); for (int i = 0; i < 32; i++) { // Prevent mono from inlining the DynamicMethod. il.Emit(CIL.OpCodes.Nop); } if (dmd.Definition.ReturnType != dmd.Definition.Module.TypeSystem.Void) { il.Body.Variables.Add(new CIL.VariableDefinition(dmd.Definition.ReturnType)); il.Emit(CIL.OpCodes.Ldloca_S, (sbyte)0); il.Emit(CIL.OpCodes.Initobj, dmd.Definition.ReturnType); il.Emit(CIL.OpCodes.Ldloc_0); } il.Emit(CIL.OpCodes.Ret); return(dmd); }
protected override void ImplementProceed(MethodDefinition methodInfo, MethodBody methodBody, ILProcessor il, FieldReference methodInfoField, MethodReference proceed, Action<ILProcessor> emitProceedTarget, MethodReference proceedTargetMethod, OpCode proceedOpCode) { if (methodInfo.IsAbstract) { CecilExtensions.CreateDefaultMethodImplementation(methodInfo, il); } else { var targetNotNull = il.Create(OpCodes.Nop); EmitProxyFromProceed(il); il.Emit(OpCodes.Ldfld, target); // Load "target" from "this" il.Emit(OpCodes.Brtrue, targetNotNull); // If target is not null, jump below base.ImplementProceed(methodInfo, methodBody, il, methodInfoField, proceed, _ => EmitProxyFromProceed(il), callBaseMethod, OpCodes.Call); il.Append(targetNotNull); // Mark where the previous branch instruction should jump to base.ImplementProceed(methodInfo, methodBody, il, methodInfoField, proceed, emitProceedTarget, proceedTargetMethod, proceedOpCode); } }
private static void Emit(ILProcessor ip, ModuleDefinition mod, VariableDefinition arrayVar, VariableDefinition indexVar, Dictionary<int, Instruction> braces, OpValue[] ops, int i) { OpValue op = ops[i]; switch (op.OpCode) { case BFOpCode.Increment: ip.Emit(OpCodes.Ldloc, arrayVar); ip.Emit(OpCodes.Ldloc, indexVar); ip.Emit(OpCodes.Ldloc, arrayVar); ip.Emit(OpCodes.Ldloc, indexVar); ip.Emit(OpCodes.Ldelem_I4); ip.Emit(OpCodes.Ldc_I4, op.Data); ip.Emit(OpCodes.Add); ip.Emit(OpCodes.Stelem_I4); break; case BFOpCode.Decrement: ip.Emit(OpCodes.Ldloc, arrayVar); ip.Emit(OpCodes.Ldloc, indexVar); ip.Emit(OpCodes.Ldloc, arrayVar); ip.Emit(OpCodes.Ldloc, indexVar); ip.Emit(OpCodes.Ldelem_I4); ip.Emit(OpCodes.Ldc_I4, op.Data); ip.Emit(OpCodes.Sub); ip.Emit(OpCodes.Stelem_I4); break; case BFOpCode.ShiftRight: ip.Emit(OpCodes.Ldloc, indexVar); ip.Emit(OpCodes.Ldc_I4, op.Data); ip.Emit(OpCodes.Add); ip.Emit(OpCodes.Stloc, indexVar); break; case BFOpCode.ShiftLeft: ip.Emit(OpCodes.Ldloc, indexVar); ip.Emit(OpCodes.Ldc_I4, op.Data); ip.Emit(OpCodes.Sub); ip.Emit(OpCodes.Stloc, indexVar); break; case BFOpCode.Output: ip.Emit(OpCodes.Ldloc, arrayVar); ip.Emit(OpCodes.Ldloc, indexVar); ip.Emit(OpCodes.Ldelem_I4); ip.Emit(OpCodes.Conv_I1); ip.Emit(OpCodes.Call, mod.Import(typeof(Console).GetMethod("Write", new[] { typeof(char) }))); break; case BFOpCode.Input: ip.Emit(OpCodes.Call, mod.Import(typeof(Console).GetMethod("Read", new Type[0]))); ip.Emit(OpCodes.Ldloc, arrayVar); ip.Emit(OpCodes.Ldloc, indexVar); ip.Emit(OpCodes.Ldelem_I4); ip.Emit(OpCodes.Conv_I1); break; case BFOpCode.CondLeft: var leftB = ip.Create(OpCodes.Ldloc, arrayVar); var rightB = ip.Create(OpCodes.Nop); ip.Append(leftB); braces[i] = leftB; braces[op.Data] = rightB; ip.Emit(OpCodes.Ldloc, indexVar); ip.Emit(OpCodes.Ldelem_I4); ip.Emit(OpCodes.Brfalse, rightB); ip.Emit(OpCodes.Nop); break; case BFOpCode.CondRight: ip.Emit(OpCodes.Br, braces[op.Data]); ip.Append(braces[i]); break; case BFOpCode.Assign: ip.Emit(OpCodes.Ldloc, arrayVar); ip.Emit(OpCodes.Ldloc, indexVar); ip.Emit(OpCodes.Ldc_I4, op.Data); ip.Emit(OpCodes.Stelem_I4); break; } }
static void EmitCall(ILProcessor il, MethodReference reference) { il.Emit(OpCodes.Call, reference); }
static void EmitCalli(ILProcessor il, MethodReference reference) { var signature = new CallSite(reference.ReturnType) { CallingConvention = MethodCallingConvention.StdCall, }; foreach (var p in reference.Parameters) { signature.Parameters.Add(p); } // Since the last parameter is always the entry point address, // we do not need any special preparation before emiting calli. il.Emit(OpCodes.Calli, signature); }
static void EmitEntryPoint(FieldDefinition entry_points, ILProcessor il, int slot) { il.Emit(OpCodes.Ldsfld, entry_points); il.Emit(OpCodes.Ldc_I4, slot); il.Emit(OpCodes.Ldelem_I); }
private void LoadActivatorField(ILProcessor cil) { cil.Emit(OpCodes.Ldarg_0); cil.Emit(OpCodes.Ldfld, _activatorField); }
/// <summary> /// Emits the instructions that will instantiate each property value and assign it to the target property. /// </summary> /// <param name="serviceMap">The service map that contains the application dependencies.</param> /// <param name="targetMethod">The target method.</param> /// <param name="module">The module that hosts the container type.</param> /// <param name="il">The <see cref="ILProcessor"/> that points to the target method body.</param> /// <param name="property">The target property.</param> /// <param name="curentDependency">The <see cref="IDependency"/> that describes the service instance that will be assigned to the target property.</param> private static void EmitPropertySetter(IDictionary<IDependency, IImplementation> serviceMap, MethodDefinition targetMethod, ModuleDefinition module, ILProcessor il, PropertyInfo property, IDependency curentDependency) { // Push the target onto the stack il.Emit(OpCodes.Dup); // Get the code that will instantiate the property value var propertyValueImplementation = serviceMap[curentDependency]; propertyValueImplementation.Emit(curentDependency, serviceMap, targetMethod); // Call the setter var setterMethod = property.GetSetMethod(); var setter = module.Import(setterMethod); var callInstruction = setterMethod.IsVirtual ? il.Create(OpCodes.Callvirt, setter) : il.Create(OpCodes.Call, setter); il.Append(callInstruction); }
static void EmitStringEpilogue(MethodDefinition wrapper, ParameterDefinition parameter, MethodBody body, ILProcessor il) { var p = parameter.ParameterType; var free = wrapper.Module.Import(TypeBindingsBase.Methods.First(m => m.Name == "FreeStringPtr")); // FreeStringPtr(ptr) var variable_name = parameter.Name + "_string_ptr"; var v = body.Variables.First(m => m.Name == variable_name); il.Emit(OpCodes.Ldloc, v.Index); il.Emit(OpCodes.Call, free); }
static void EmitStringArrayParameter(MethodDefinition wrapper, ParameterDefinition parameter, MethodBody body, ILProcessor il) { var p = parameter.ParameterType; // string[] masrhaling: // IntPtr ptr = MarshalStringArrayToPtr(strings); // try { calli } // finally { FreeStringArrayPtr(ptr); } var marshal_str_array_to_ptr = wrapper.Module.Import(TypeBindingsBase.Methods.First(m => m.Name == "MarshalStringArrayToPtr")); // IntPtr ptr; var variable_name = parameter.Name + "_string_array_ptr"; body.Variables.Add(new VariableDefinition(variable_name, TypeIntPtr)); int index = body.Variables.Count - 1; // ptr = MarshalStringArrayToPtr(strings); il.Emit(OpCodes.Call, marshal_str_array_to_ptr); il.Emit(OpCodes.Stloc, index); il.Emit(OpCodes.Ldloc, index); // The finally block will be emitted in the function epilogue }
protected override void ImplementBody(ILProcessor il, FieldReference methodInfoField, FieldReference propertyInfoField, MethodReference proceed, MethodReference proceedTargetMethod) { // If it's abstract, then the method is entirely implemented by the InvocationHandler if (Method.IsAbstract) { base.ImplementBody(il, methodInfoField, propertyInfoField, proceed, proceedTargetMethod); } // Otherwise, it is implemented by the class itself, and calling this.Invocation().Proceed() calls the InvocationHandler else { Method.Body.InitLocals = true; // First declare the invocation in a private local variable var invocation = new VariableDefinition(ClassWeaver.Context.InvocationType); var instructions = Method.Body.Instructions.ToList(); Method.Body.Instructions.Clear(); // Wipe out the existing instructions for the method Method.Body.Variables.Add(invocation); // Add a variable to store the invocation EmitInvocation(il, methodInfoField, propertyInfoField, proceed); // Put the invocation on the stack il.Emit(OpCodes.Dup); // Duplicate invocation for below il.Emit(OpCodes.Stloc, invocation); // Store the invocation in the variable declared (just) earlier // Add the invocation to the end of the array il.Emit(OpCodes.Call, ClassWeaver.Context.InvocationGetArguments); // Array now on the stack with the invocation above it il.Emit(OpCodes.Ldc_I4, Method.Parameters.Count); // Array index il.Emit(OpCodes.Ldloc, invocation); // Element value il.Emit(OpCodes.Stelem_Any, ClassWeaver.Context.ModuleDefinition.TypeSystem.Object); // Set array at index to element value // Special instrumentation for async methods var returnType = Method.ReturnType; if (ClassWeaver.Context.TaskType.IsAssignableFrom(returnType)) { // If the return type is Task<T> if (returnType.IsTaskT()) { var actualReturnType = returnType.GetTaskType(); var expectedAsyncBuilder = ClassWeaver.Context.AsyncTaskMethodBuilder.MakeGenericInstanceType(actualReturnType); // Now find the call to .Start() (only will be found if we're async) var startInstructionMethod = (GenericInstanceMethod)instructions .Where(x => x.OpCode == OpCodes.Call) .Select(x => (MethodReference)x.Operand) .Where(x => x.IsGenericInstance && x.Name == "Start" && x.DeclaringType.CompareTo(expectedAsyncBuilder)) .SingleOrDefault(); if (startInstructionMethod != null) { var asyncType = startInstructionMethod.GenericArguments[0]; var invocationField = InstrumentAsyncType(asyncType); // Find the first instruction that is setting a field on the async type. We can just assume it's a three instruction // set (it always is in this context) And we add our instructions to set the invocation field. var nextSetFieldIndex = instructions.IndexOf(x => x.OpCode == OpCodes.Stfld && x.Operand is FieldDefinition && ((FieldDefinition)x.Operand).DeclaringType.CompareTo(asyncType)); if (nextSetFieldIndex == -1) throw new Exception($"Could not find expected stfld of async type: {asyncType}"); var setFieldLoadInstance = instructions[nextSetFieldIndex - 2]; instructions.Insert(nextSetFieldIndex - 2, il.Clone(setFieldLoadInstance)); instructions.Insert(nextSetFieldIndex - 1, il.Create(OpCodes.Ldloc, invocation)); instructions.Insert(nextSetFieldIndex, il.Create(OpCodes.Stfld, invocationField)); } } } InstrumentInstructions(il, instructions, 2, x => x.Emit(OpCodes.Ldloc, invocation)); } }
static void EmitStringBuilderEpilogue(MethodDefinition wrapper, MethodDefinition native, ParameterDefinition parameter, MethodBody body, ILProcessor il) { var p = parameter.ParameterType; if (p.Name == "StringBuilder") { // void GetShaderInfoLog(..., StringBuilder foo) // try { // foo_sb_ptr = Marshal.AllocHGlobal(sb.Capacity + 1); -- already emitted // glGetShaderInfoLog(..., foo_sb_ptr); -- already emitted // MarshalPtrToStringBuilder(foo_sb_ptr, foo); // } // finally { // Marshal.FreeHGlobal(foo_sb_ptr); // } // Make sure we have imported BindingsBase::MasrhalPtrToStringBuilder and Marshal::FreeHGlobal var ptr_to_sb = wrapper.Module.Import(TypeBindingsBase.Methods.First(m => m.Name == "MarshalPtrToStringBuilder")); var free_hglobal = wrapper.Module.Import(TypeMarshal.Methods.First(m => m.Name == "FreeHGlobal")); var block = new ExceptionHandler(ExceptionHandlerType.Finally); block.TryStart = body.Instructions[0]; var variable_name = parameter.Name + " _sb_ptr"; var v = body.Variables.First(m => m.Name == variable_name); il.Emit(OpCodes.Ldloc, v.Index); il.Emit(OpCodes.Ldarg, parameter.Index); il.Emit(OpCodes.Call, ptr_to_sb); block.TryEnd = body.Instructions.Last(); block.HandlerStart = body.Instructions.Last(); il.Emit(OpCodes.Ldloc, v.Index); il.Emit(OpCodes.Call, free_hglobal); block.HandlerEnd = body.Instructions.Last(); } }
private static void EmitReturnTypeWrapper(MethodDefinition wrapper, MethodDefinition native, MethodBody body, ILProcessor il) { if (wrapper.Parameters.Count < native.Parameters.Count) { // Convenience wrapper. The result is stored in the last local variable il.Emit(OpCodes.Ldloc, body.Variables.Count - 1); } else if (wrapper.ReturnType != native.ReturnType) { if (wrapper.ReturnType.Name == "String") { // String return-type wrapper // return new string((sbyte*)((void*)GetString())); var intptr_to_voidpointer = wrapper.Module.Import(mscorlib.MainModule.GetType("System.IntPtr").GetMethods() .First(m => { return m.Name == "op_Explicit" && m.ReturnType.Name == "Void*"; })); var string_constructor = wrapper.Module.Import(mscorlib.MainModule.GetType("System.String").GetConstructors() .First(m => { var p = m.Parameters; return p.Count > 0 && p[0].ParameterType.Name == "SByte*"; })); il.Emit(OpCodes.Call, intptr_to_voidpointer); il.Emit(OpCodes.Newobj, string_constructor); } else if (wrapper.ReturnType.Resolve().IsEnum) { // Nothing to do } else if (wrapper.ReturnType.Name == "Boolean" && native.ReturnType.Name == "Byte") { // Nothing to do // It appears that a byte with 1 = true (GL_TRUE) and 0 = false (GL_FALSE) // can be reinterpreted as a bool without a problem. // Todo: maybe we should return (value == 0 ? false : true) just to be // on the safe side? } else { Console.Error.WriteLine("Return wrapper for '{1}' not implemented yet ({0})", native.Name, wrapper.ReturnType.Name); } } else { // nothing to do, the native call leaves the return value // on the stack and we return that unmodified to the caller. } }
static void EmitDebugEpilogue(MethodDefinition wrapper, ILProcessor il, DebugVariables vars) { if (vars != null) { var disposeMethod = vars.ErrorHelperType.Methods.First( method => method.Name == "Dispose"); // Store then reload the result from the call var resultLocal = new VariableDefinition(wrapper.ReturnType); if (resultLocal.VariableType.FullName != Program.TypeVoid.FullName) { il.Body.Variables.Add(resultLocal); il.Emit(OpCodes.Stloc, resultLocal); } // Special case End to turn on error checking. if (il.Body.Method.Name == "End") { il.Emit(OpCodes.Call, vars.Get_CurrentContext); il.Emit(OpCodes.Ldc_I4_1); il.Emit(OpCodes.Conv_I1); il.Emit(OpCodes.Call, vars.Set_ErrorChecking); } // We need a NOP to set up the finally handler range correctly. var nopInstruction = Instruction.Create(OpCodes.Nop); var loadInstruction = Instruction.Create(OpCodes.Ldloca, vars.ErrorHelperLocal); var disposeInstruction = Instruction.Create(OpCodes.Call, disposeMethod); var endFinallyInstruction = Instruction.Create(OpCodes.Endfinally); var endTryInstruction = Instruction.Create(OpCodes.Leave, nopInstruction); il.Append(endTryInstruction); il.Append(loadInstruction); il.Append(disposeInstruction); il.Append(endFinallyInstruction); il.Append(nopInstruction); var finallyHandler = new ExceptionHandler(ExceptionHandlerType.Finally); finallyHandler.TryStart = vars.BeginTry; finallyHandler.TryEnd = loadInstruction; finallyHandler.HandlerStart = loadInstruction; finallyHandler.HandlerEnd = nopInstruction; il.Body.ExceptionHandlers.Add(finallyHandler); if (resultLocal.VariableType.FullName != Program.TypeVoid.FullName) { il.Emit(OpCodes.Ldloc, resultLocal); } } }
static DebugVariables EmitDebugPrologue(MethodDefinition wrapper, ILProcessor il) { DebugVariables vars = null; if (il.Body.Method.Name != "GetError") { // Pull out the namespace name, method fullname will look // something like "type namespace.class::method(type arg)" var module = il.Body.Method.FullName; module = module.Substring(module.IndexOf(' ') + 1); module = module.Substring(0, module.IndexOf("::")); module = module.Substring(0, module.LastIndexOf('.')); // Only works for Graphics modules due to hardcoded use of // OpenTK.Graphics.GraphicsContext if (module == "OpenTK.Graphics.OpenGL4" || module == "OpenTK.Graphics.OpenGL" || module == "OpenTK.Graphics.ES10" || module == "OpenTK.Graphics.ES11" || module == "OpenTK.Graphics.ES20" || module == "OpenTK.Graphics.ES30") { var errorHelperType = wrapper.Module.GetType(module, "ErrorHelper"); if (errorHelperType != null) { vars = new DebugVariables(); vars.ErrorHelperType = errorHelperType; // GraphicsContext type var graphicsContext = wrapper.Module.Types.First( type => type.FullName == "OpenTK.Graphics.GraphicsContext"); // IGraphicsContext type var iGraphicsContext = wrapper.Module.Types.First( type => type.FullName == "OpenTK.Graphics.IGraphicsContext"); // Get the constructor that takes a GraphicsContext parameter var ctor = vars.ErrorHelperType.GetConstructors().FirstOrDefault( c => c.Parameters.Count == 1 && c.Parameters[0].ParameterType.FullName == iGraphicsContext.FullName); if (ctor == null) { throw new InvalidOperationException( String.Format( "{0} does needs a constructor taking {1}", errorHelperType, graphicsContext)); } // GraphicsContext.CurrentContext property getter vars.Get_CurrentContext = graphicsContext.Methods.First( method => method.Name == "get_CurrentContext"); vars.Set_ErrorChecking = graphicsContext.Methods.First( method => method.Name == "set_ErrorChecking"); vars.ErrorHelperLocal = new VariableDefinition(vars.ErrorHelperType); // using (new ErrorHelper(GraphicsContext.CurrentContext)) { ... il.Body.Variables.Add(vars.ErrorHelperLocal); il.Emit(OpCodes.Ldloca, vars.ErrorHelperLocal); il.Emit(OpCodes.Call, vars.Get_CurrentContext); il.Emit(OpCodes.Call, ctor); vars.BeginTry = Instruction.Create(OpCodes.Nop); il.Append(vars.BeginTry); // Special case Begin to turn off error checking. if (il.Body.Method.Name == "Begin") { il.Emit(OpCodes.Call, vars.Get_CurrentContext); il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Conv_I1); il.Emit(OpCodes.Call, vars.Set_ErrorChecking); } } } } return vars; }
/// <summary> /// Emits the IL that calculates a hash code from a given service type. /// </summary> /// <param name="module">The module that holds the target type.</param> /// <param name="body">The body of the GetServiceHashCode method.</param> /// <param name="il">The <see cref="ILProcessor"/> that will be used to emit the instructions.</param> /// <param name="getHashCodeMethod">The <see cref="Object.GetHashCode"/> method.</param> /// <returns>The variable that holds the hash code.</returns> private static VariableDefinition EmitGetServiceTypeHashCode(ModuleDefinition module, Mono.Cecil.Cil.MethodBody body, ILProcessor il, MethodReference getHashCodeMethod) { // Get the hash code for the service type var hashVariable = AddLocals(module, body); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Callvirt, getHashCodeMethod); il.Emit(OpCodes.Stloc, hashVariable); return hashVariable; }
static void EmitStringArrayEpilogue(MethodDefinition wrapper, ParameterDefinition parameter, MethodBody body, ILProcessor il) { // Note: only works for string vectors (1d arrays). // We do not (and will probably never) support 2d or higher string arrays var p = parameter.ParameterType; var free = wrapper.Module.Import(TypeBindingsBase.Methods.First(m => m.Name == "FreeStringArrayPtr")); // FreeStringArrayPtr(string_array_ptr, string_array.Length) var variable_name = parameter.Name + "_string_array_ptr"; var v = body.Variables.First(m => m.Name == variable_name); // load string_array_ptr il.Emit(OpCodes.Ldloc, v.Index); // load string_array.Length il.Emit(OpCodes.Ldarg, parameter.Index); il.Emit(OpCodes.Ldlen); il.Emit(OpCodes.Conv_I4); // call FreeStringArrayPtr il.Emit(OpCodes.Call, free); }
/// <summary> /// Emits the IL that calculates a hash code from a given service name. /// </summary> /// <param name="il">The <see cref="ILProcessor"/> that will be used to emit the instructions.</param> /// <param name="getHashCodeMethod">The <see cref="Object.GetHashCode"/> method.</param> /// <param name="hashVariable">The local variable that will store the hash code.</param> private static void EmitGetServiceNameHashCode(ILProcessor il, MethodReference getHashCodeMethod, VariableDefinition hashVariable) { il.Emit(OpCodes.Ldloc, hashVariable); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Callvirt, getHashCodeMethod); il.Emit(OpCodes.Xor); il.Emit(OpCodes.Stloc, hashVariable); }
static void EmitConvenienceWrapper(MethodDefinition wrapper, MethodDefinition native, int difference, MethodBody body, ILProcessor il) { if (wrapper.Parameters.Count > 2) { // Todo: emit all parameters bar the last two throw new NotImplementedException(); } if (wrapper.ReturnType.Name != "Void") { if (difference == 2) { // Convert sized out-array/reference to return value, for example: // void GenTextures(int n, int[] textures) -> int GenTexture() // { // const int n = 1; // int buffers; // calli GenTextures(n, &textures); // return result; // } body.Variables.Add(new VariableDefinition(wrapper.ReturnType)); il.Emit(OpCodes.Ldc_I4, 1); // const int n = 1 il.Emit(OpCodes.Ldloca, body.Variables.Count - 1); // &buffers } else if (difference == 1) { // Convert unsized out-array/reference to return value, for example: // void GetBoolean(GetPName pname, out bool data) -> bool GetBoolean(GetPName pname) // { // bool result; // GetBooleanv(pname, &result); // return result; // } body.Variables.Add(new VariableDefinition(wrapper.ReturnType)); EmitParameters(wrapper, native, body, il); il.Emit(OpCodes.Ldloca, body.Variables.Count - 1); } else { Console.Error.WriteLine("Unknown wrapper type for ({0})", native.Name); } } else { if (difference == 1) { // Convert in-array/reference to single element, for example: // void DeleteTextures(int n, ref int textures) -> void DeleteTexture(int texture) // { // const int n = 1; // calli DeleteTextures(n, &textures); // } il.Emit(OpCodes.Ldc_I4, 1); // const int n = 1 il.Emit(OpCodes.Ldarga, wrapper.Parameters.Last()); // &textures } else { Console.Error.WriteLine("Unknown wrapper type for ({0})", native.Name); } } }
protected override void EmitOptOutTarget(ILProcessor il) { il.Emit(OpCodes.Ldarg_0); }
static int EmitParameters(MethodDefinition method, MethodDefinition native, MethodBody body, ILProcessor il) { int i; for (i = 0; i < method.Parameters.Count; i++) { var parameter = method.Parameters[i]; var p = method.Module.Import(method.Parameters[i].ParameterType); il.Emit(OpCodes.Ldarg, i); if (p.Name.Contains("Int32") && native.Parameters[i].ParameterType.Name.Contains("IntPtr")) { // This is a convenience Int32 overload for an IntPtr (size_t) parameter. // We need to convert the loaded argument to IntPtr. il.Emit(OpCodes.Conv_I); } else if (p.Name == "StringBuilder") { EmitStringBuilderParameter(method, parameter, body, il); } else if (p.Name == "String" && !p.IsArray) { EmitStringParameter(method, parameter, body, il); } else if (p.IsByReference) { body.Variables.Add(new VariableDefinition(new PinnedType(p))); var index = body.Variables.Count - 1; il.Emit(OpCodes.Stloc, index); il.Emit(OpCodes.Ldloc, index); il.Emit(OpCodes.Conv_I); } else if (p.IsArray) { if (p.Name != method.Module.Import(typeof(string[])).Name) { // .Net treats 1d arrays differently than higher rank arrays. // 1d arrays are directly supported by instructions such as ldlen and ldelema. // Higher rank arrays must be accessed through System.Array methods such as get_Length. // 1d array: // check array is not null // check ldlen array > 0 // ldc.i4.0 // ldelema // 2d array: // check array is not null // check array.get_Length() > 0 // ldc.i4.0 // ldc.i4.0 // call instance T& T[0..., 0...]::Address(int32, int32) // Mono treats everything as a 1d array. // Interestingly, the .Net approach works on both Mono and .Net. // The Mono approach fails when using high-rank arrays on .Net. // We should report a bug to http://bugzilla.xamarin.com // Pin the array and pass the address // of its first element. var array = (ArrayType)p; var element_type = p.GetElementType(); body.Variables.Add(new VariableDefinition(new PinnedType(new ByReferenceType(element_type)))); int pinned_index = body.Variables.Count - 1; var empty = il.Create(OpCodes.Ldc_I4, 0); var pin = il.Create(OpCodes.Ldarg, i); var end = il.Create(OpCodes.Stloc, pinned_index); // if (array == null) goto empty il.Emit(OpCodes.Brfalse, empty); // else if (array.Length != 0) goto pin il.Emit(OpCodes.Ldarg, i); if (array.Rank == 1) { il.Emit(OpCodes.Ldlen); il.Emit(OpCodes.Conv_I4); } else { var get_length = method.Module.Import( mscorlib.MainModule.GetType("System.Array").Methods.First(m => m.Name == "get_Length")); il.Emit(OpCodes.Callvirt, get_length); } il.Emit(OpCodes.Brtrue, pin); // empty: IntPtr ptr = IntPtr.Zero il.Append(empty); il.Emit(OpCodes.Conv_U); il.Emit(OpCodes.Br, end); // pin: &array[0] il.Append(pin); if (array.Rank == 1) { // 1d array (vector), address is taken by ldelema il.Emit(OpCodes.Ldc_I4, 0); il.Emit(OpCodes.Ldelema, element_type); } else { // 2d-3d array, address must be taken as follows: // call instance T& T[0..., 0..., 0...]::Address(int, int, int) ByReferenceType t_ref = array.ElementType.MakeByReferenceType(); MethodReference get_address = new MethodReference("Address", t_ref, array); for (int r = 0; r < array.Rank; r++) { get_address.Parameters.Add(new ParameterDefinition(TypeInt32)); } get_address.HasThis = true; // emit the get_address call for (int r = 0; r < array.Rank; r++) { il.Emit(OpCodes.Ldc_I4, 0); } il.Emit(OpCodes.Call, get_address); } // end: fixed (IntPtr ptr = &array[0]) il.Append(end); il.Emit(OpCodes.Ldloc, pinned_index); il.Emit(OpCodes.Conv_I); } else { EmitStringArrayParameter(method, parameter, body, il); } } } return i; }
static void EmitStringBuilderParameter(MethodDefinition method, ParameterDefinition parameter, MethodBody body, ILProcessor il) { var p = parameter.ParameterType; // void GetShaderInfoLog(..., StringBuilder foo) // IntPtr foo_sb_ptr; // try { // foo_sb_ptr = Marshal.AllocHGlobal(sb.Capacity + 1); // glGetShaderInfoLog(..., foo_sb_ptr); // MarshalPtrToStringBuilder(foo_sb_ptr, sb); // } // finally { // Marshal.FreeHGlobal(sb_ptr); // } // Make sure we have imported StringBuilder::Capacity and Marshal::AllocHGlobal var sb_get_capacity = method.Module.Import(TypeStringBuilder.Methods.First(m => m.Name == "get_Capacity")); var alloc_hglobal = method.Module.Import(TypeMarshal.Methods.First(m => m.Name == "AllocHGlobal")); // IntPtr ptr; var variable_name = parameter.Name + " _sb_ptr"; body.Variables.Add(new VariableDefinition(variable_name, TypeIntPtr)); int index = body.Variables.Count - 1; // ptr = Marshal.AllocHGlobal(sb.Capacity + 1); il.Emit(OpCodes.Callvirt, sb_get_capacity); il.Emit(OpCodes.Call, alloc_hglobal); il.Emit(OpCodes.Stloc, index); il.Emit(OpCodes.Ldloc, index); // We'll emit the try-finally block in the epilogue implementation, // because we haven't yet emitted all necessary instructions here. }
/// <summary> /// Emits the instructions that call <see cref="IInitialize.Initialize"/> on a given service instance. /// </summary> /// <param name="il"></param> /// <param name="module">The host module.</param> /// <param name="serviceInstance">The local variable that points to the current service instance.</param> public void Initialize(ILProcessor il, ModuleDefinition module, VariableDefinition serviceInstance) { var body = il.Body; var method = body.Method; var declaringType = method.DeclaringType; var targetField = GetTargetField(declaringType); if (targetField == null) return; var initializeType = module.ImportType<IInitialize>(); il.Emit(OpCodes.Ldloc, serviceInstance); il.Emit(OpCodes.Isinst, initializeType); var initializeMethod = module.ImportMethod<IInitialize>("Initialize"); var skipInitializationCall = il.Create(OpCodes.Nop); il.Emit(OpCodes.Brfalse, skipInitializationCall); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, targetField); GetServiceHash(il, module, serviceInstance); var containsMethod = module.ImportMethod<Dictionary<int, int>>("ContainsKey"); il.Emit(OpCodes.Callvirt, containsMethod); il.Emit(OpCodes.Brtrue, skipInitializationCall); // if (!__initializedServices.ContainsKey(currentService.GetHashCode()) { il.Emit(OpCodes.Ldloc, serviceInstance); il.Emit(OpCodes.Isinst, initializeType); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Callvirt, initializeMethod); // __initializedServices.Add(hashCode, 0); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, targetField); GetServiceHash(il, module, serviceInstance); il.Emit(OpCodes.Ldc_I4_1); var addMethod = module.ImportMethod<Dictionary<int, int>>("Add"); il.Emit(OpCodes.Callvirt, addMethod); il.Append(skipInitializationCall); }