/// <summary> /// Emits the instructions that will instantiate the current implementation. /// </summary> /// <param name="dependency">The dependency that describes the service to be instantiated.</param> /// <param name="serviceMap">The service map that contains the list of dependencies in the application.</param> /// <param name="targetMethod">The target method.</param> public void Emit(IDependency dependency, IDictionary<IDependency, IImplementation> serviceMap, MethodDefinition targetMethod) { var declaringType = targetMethod.DeclaringType; var module = declaringType.Module; var microContainerType = module.ImportType<IMicroContainer>(); var il = targetMethod.GetILGenerator(); // if (this is IMicroContainer && this.NextContainer != null) { il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Isinst, microContainerType); var skipCreate = il.Create(OpCodes.Nop); il.Emit(OpCodes.Brfalse, skipCreate); EmitGetContainerInstance(module, microContainerType, il, skipCreate); var getInstance = module.ImportMethod<IMicroContainer>("GetInstance"); var getTypeFromHandleMethod = typeof(System.Type).GetMethod("GetTypeFromHandle", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static); var getTypeFromHandle = module.Import(getTypeFromHandleMethod); // Push the service type onto the stack var serviceType = module.Import(_serviceType); il.Emit(OpCodes.Ldtoken, serviceType); il.Emit(OpCodes.Call, getTypeFromHandle); var loadString = string.IsNullOrEmpty(_serviceName) ? il.Create(OpCodes.Ldnull) : il.Create(OpCodes.Ldstr, _serviceName); il.Append(loadString); il.Emit(OpCodes.Callvirt, getInstance); var endLabel = il.Create(OpCodes.Nop); il.Emit(OpCodes.Br, endLabel); il.Append(skipCreate); var serviceNotFoundExceptionCtor = module.ImportConstructor<ServiceNotFoundException>(typeof(string), typeof(System.Type)); var serviceName = dependency.ServiceName ?? string.Empty; il.Emit(OpCodes.Ldstr, serviceName); il.Emit(OpCodes.Ldtoken, serviceType); il.Emit(OpCodes.Call, getTypeFromHandle); il.Emit(OpCodes.Newobj, serviceNotFoundExceptionCtor); il.Emit(OpCodes.Throw); il.Append(endLabel); // } }
/// <summary> /// Emits the instructions that will instantiate the current implementation. /// </summary> /// <param name="dependency">The dependency that describes the service to be instantiated.</param> /// <param name="serviceMap">The service map that contains the list of dependencies in the application.</param> /// <param name="targetMethod">The target method.</param> public void Emit(IDependency dependency, IDictionary<IDependency, IImplementation> serviceMap, MethodDefinition targetMethod) { var declaringType = targetMethod.DeclaringType; var module = declaringType.Module; var listType = typeof(List<>).MakeGenericType(_serviceType); var listCtor = module.ImportConstructor(listType, new Type[0]); var listVariable = targetMethod.AddLocal(listType); var IL = targetMethod.GetILGenerator(); IL.Emit(OpCodes.Newobj, listCtor); IL.Emit(OpCodes.Stloc, listVariable); var targetDependencies = (from d in serviceMap.Keys where d.ServiceType == _serviceType select d).ToArray(); var addItem = module.ImportMethod("Add", listType); var serviceType = module.Import(_serviceType); var currentService = targetMethod.AddLocal(_serviceType); foreach(var currentDependency in targetDependencies) { IL.Emit(OpCodes.Ldloc, listVariable); // Instantiate the current service type var implementation = new ContainerCall(currentDependency.ServiceType, currentDependency.ServiceName); implementation.Emit(currentDependency, serviceMap, targetMethod); IL.Emit(OpCodes.Isinst, serviceType); IL.Emit(OpCodes.Stloc, currentService); // Call IInitialize.Initialize(container) on the current service type _initializer.Initialize(IL, module, currentService); IL.Emit(OpCodes.Ldloc, currentService); IL.Emit(OpCodes.Callvirt, addItem); } var enumerableType = typeof(IEnumerable<>).MakeGenericType(_serviceType); var importedEnumerableType = module.Import(enumerableType); IL.Emit(OpCodes.Ldloc, listVariable); IL.Emit(OpCodes.Isinst, importedEnumerableType); }
/// <summary> /// Emits the <see cref="IFactory{T}.Create"/> method call that will instantiate the current service instance. /// </summary> /// <param name="dependency">The dependency that describes the service to be instantiated.</param> /// <param name="serviceMap">The service map that contains the list of dependencies in the application.</param> /// <param name="targetMethod">The target method.</param> public void Emit(IDependency dependency, IDictionary<IDependency, IImplementation> serviceMap, MethodDefinition targetMethod) { var factoryType = typeof (IFactory<>).MakeGenericType(_serviceType); var getFactoryInstanceCall = new ContainerCall(factoryType, _serviceName); var factoryName = _serviceName; getFactoryInstanceCall.Emit(new Dependency(factoryType, factoryName), serviceMap, targetMethod); var declaringType = targetMethod.DeclaringType; var module = declaringType.Module; var factoryTypeReference = module.Import(factoryType); var createMethod = module.Import(factoryType.GetMethod("Create")); var IL = targetMethod.GetILGenerator(); IL.Emit(OpCodes.Isinst, factoryTypeReference); IL.Emit(OpCodes.Callvirt, createMethod); }
/// <summary> /// Emits a service as a singleton type. /// </summary> /// <param name="targetMethod">The <see cref="IMicroContainer.GetInstance"/> method implementation.</param> /// <param name="dependency">The dependency that will be instantiated by the container.</param> /// <param name="implementation">The implementation that will be used to instantiate the dependency.</param> /// <param name="serviceMap">The service map the contains the current application dependencies.</param> public void EmitService(MethodDefinition targetMethod, IDependency dependency, IImplementation implementation, IDictionary<IDependency, IImplementation> serviceMap) { MethodDefinition getInstanceMethod = null; var worker = targetMethod.GetILGenerator(); // Emit only one singleton per dependency and call // the singleton GetInstance() method on every subsequent emit call if (_entries.ContainsKey(dependency)) { getInstanceMethod = _entries[dependency]; worker.Emit(OpCodes.Call, getInstanceMethod); return; } var declaringType = targetMethod.DeclaringType; var module = declaringType.Module; var serviceType = dependency.ServiceType; var typeName = serviceType.Name; var singletonName = string.Format("{0}ServiceSingleton-{1}", typeName, dependency.GetHashCode()); const TypeAttributes typeAttributes = TypeAttributes.NotPublic | TypeAttributes.AutoClass | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit; var objectType = module.Import(typeof(object)); var singletonType = AddDefaultSingletonConstructor(module, singletonName, typeAttributes, objectType); var instanceField = new FieldDefinition("__instance", FieldAttributes.Assembly | FieldAttributes.InitOnly | FieldAttributes.Static, objectType); DefineNestedType(module, singletonType, instanceField, serviceMap, implementation, dependency, targetMethod); getInstanceMethod = DefineGetInstance(singletonType, worker, instanceField); worker.Emit(OpCodes.Call, getInstanceMethod); var serviceTypeRef = module.Import(serviceType); worker.Emit(OpCodes.Unbox_Any, serviceTypeRef); // Cache the singleton method _entries[dependency] = getInstanceMethod; }
/// <summary> /// Emits the instructions that will instantiate the current implementation. /// </summary> /// <param name="dependency">The dependency that describes the service to be instantiated.</param> /// <param name="serviceMap">The service map that contains the list of dependencies in the application.</param> /// <param name="targetMethod">The target method.</param> public void Emit(IDependency dependency, IDictionary<IDependency, IImplementation> serviceMap, MethodDefinition targetMethod) { var IL = targetMethod.GetILGenerator(); IL.Emit(OpCodes.Ldarg_0); }
/// <summary> /// Emits the IL to save information about /// the method currently being executed. /// </summary> /// <seealso cref="IInvocationInfo"/> /// <param name="targetMethod">The target method currently being executed.</param> /// <param name="interceptedMethod">The method that will be passed to the <paramref name="invocationInfo"/> as the currently executing method.</param> /// <param name="invocationInfo">The local variable that will store the resulting <see cref="IInvocationInfo"/> instance.</param> public void Emit(MethodDefinition targetMethod, MethodReference interceptedMethod, VariableDefinition invocationInfo) { var module = targetMethod.DeclaringType.Module; var currentMethod = MethodDefinitionExtensions.AddLocal(targetMethod, typeof(MethodBase)); var parameterTypes = MethodDefinitionExtensions.AddLocal(targetMethod, typeof(Type[])); var arguments = MethodDefinitionExtensions.AddLocal(targetMethod, typeof(object[])); var typeArguments = MethodDefinitionExtensions.AddLocal(targetMethod, typeof(Type[])); var systemType = module.ImportType(typeof(Type)); var IL = targetMethod.GetILGenerator(); #region Initialize the InvocationInfo constructor arguments // Type[] typeArguments = new Type[genericTypeCount]; var genericParameterCount = targetMethod.GenericParameters.Count; IL.Emit(OpCodes.Ldc_I4, genericParameterCount); IL.Emit(OpCodes.Newarr, systemType); IL.Emit(OpCodes.Stloc, typeArguments); // object[] arguments = new object[argumentCount]; IL.PushArguments(targetMethod, module, arguments); // object target = this; if (targetMethod.HasThis) IL.Emit(OpCodes.Ldarg_0); else IL.Emit(OpCodes.Ldnull); IL.PushMethod(interceptedMethod, module); IL.Emit(OpCodes.Stloc, currentMethod); // MethodBase targetMethod = currentMethod as MethodBase; IL.Emit(OpCodes.Ldloc, currentMethod); // Push the generic type arguments onto the stack if (genericParameterCount > 0) IL.PushGenericArguments(targetMethod, module, typeArguments); // Make sure that the generic methodinfo is instantiated with the // proper type arguments if (targetMethod.GenericParameters.Count > 0) { var methodInfoType = module.Import(typeof(MethodInfo)); IL.Emit(OpCodes.Isinst, methodInfoType); var getIsGenericMethodDef = module.ImportMethod<MethodInfo>("get_IsGenericMethodDefinition"); IL.Emit(OpCodes.Dup); IL.Emit(OpCodes.Callvirt, getIsGenericMethodDef); // Determine if the current method is a generic method // definition var skipMakeGenericMethod = IL.Create(OpCodes.Nop); IL.Emit(OpCodes.Brfalse, skipMakeGenericMethod); // Instantiate the specific generic method instance var makeGenericMethod = module.ImportMethod<MethodInfo>("MakeGenericMethod", typeof(Type[])); IL.Emit(OpCodes.Ldloc, typeArguments); IL.Emit(OpCodes.Callvirt, makeGenericMethod); IL.Append(skipMakeGenericMethod); } if (_pushStackTrace) IL.PushStackTrace(module); else IL.Emit(OpCodes.Ldnull); // Save the parameter types IL.Emit(OpCodes.Ldc_I4, targetMethod.Parameters.Count); IL.Emit(OpCodes.Newarr, systemType); IL.Emit(OpCodes.Stloc, parameterTypes); IL.SaveParameterTypes(targetMethod, module, parameterTypes); IL.Emit(OpCodes.Ldloc, parameterTypes); // Push the type arguments back onto the stack IL.Emit(OpCodes.Ldloc, typeArguments); // Save the return type var getTypeFromHandle = module.Import(_getTypeFromHandle); var returnType = targetMethod.ReturnType.ReturnType; IL.Emit(OpCodes.Ldtoken, returnType); IL.Emit(OpCodes.Call, getTypeFromHandle); // Push the arguments back onto the stack IL.Emit(OpCodes.Ldloc, arguments); #endregion // InvocationInfo info = new InvocationInfo(...); var infoConstructor = module.Import(_invocationInfoConstructor); IL.Emit(OpCodes.Newobj, infoConstructor); IL.Emit(OpCodes.Stloc, invocationInfo); }
/// <summary> /// Creates a property setter method implementation with the /// <paramref name="propertyType"/> as the setter parameter. /// </summary> /// <param name="propertyType">Represents the <see cref="TypeReference">parameter type</see> for the setter method.</param> /// <param name="attributes">The method attributes associated with the setter method.</param> /// <param name="backingField">The field that will store the instance for the setter method.</param> /// <param name="setterName">The method name of the setter method.</param> /// <param name="voidType">The <see cref="TypeReference"/> that represents <see cref="Void"/>.</param> /// <returns>A <see cref="MethodDefinition"/> that represents the setter method itself.</returns> private static MethodDefinition AddPropertySetter(TypeReference propertyType, MethodAttributes attributes, FieldReference backingField, string setterName, TypeReference voidType) { var setter = new MethodDefinition(setterName, attributes, voidType) { IsPublic = true, ImplAttributes = (MethodImplAttributes.Managed | MethodImplAttributes.IL) }; setter.Parameters.Add(new ParameterDefinition(propertyType)); var IL = setter.GetILGenerator(); IL.Emit(OpCodes.Ldarg_0); IL.Emit(OpCodes.Ldarg_1); IL.Emit(OpCodes.Stfld, backingField); IL.Emit(OpCodes.Ret); return setter; }
/// <summary> /// Creates a property getter method implementation with the /// <paramref name="propertyType"/> as the return type. /// </summary> /// <param name="propertyType">Represents the <see cref="TypeReference">return type</see> for the getter method.</param> /// <param name="getterName">The getter method name.</param> /// <param name="attributes">The method attributes associated with the getter method.</param> /// <param name="backingField">The field that will store the instance that the getter method will retrieve.</param> /// <returns>A <see cref="MethodDefinition"/> representing the getter method itself.</returns> private static MethodDefinition AddPropertyGetter(TypeReference propertyType, string getterName, MethodAttributes attributes, FieldReference backingField) { var getter = new MethodDefinition(getterName, attributes, propertyType) { IsPublic = true, ImplAttributes = (MethodImplAttributes.Managed | MethodImplAttributes.IL) }; var IL = getter.GetILGenerator(); IL.Emit(OpCodes.Ldarg_0); IL.Emit(OpCodes.Ldfld, backingField); IL.Emit(OpCodes.Ret); return getter; }
/// <summary> /// Saves the current <see cref="IExceptionHandlerInfo"/> instance. /// </summary> /// <param name="targetMethod">The target method.</param> /// <param name="emitter">The <see cref="IEmitInvocationInfo"/> instance that will emit the current method context.</param> private void SaveExceptionInfo(MethodDefinition targetMethod, IEmitInvocationInfo emitter) { var IL = targetMethod.GetILGenerator(); var module = IL.GetModule(); emitter.Emit(targetMethod, targetMethod, _invocationInfo); IL.Emit(OpCodes.Ldloc, _exception); IL.Emit(OpCodes.Ldloc, _invocationInfo); var exceptionInfoConstructor = module.ImportConstructor<ExceptionHandlerInfo>( typeof (Exception), typeof (IInvocationInfo)); IL.Emit(OpCodes.Newobj, exceptionInfoConstructor); IL.Emit(OpCodes.Stloc, _exceptionInfo); var returnType = targetMethod.ReturnType.ReturnType; if (returnType == _voidType || _returnValue == null) return; // exceptionInfo.ReturnValue = returnValue; var setReturnValue = module.ImportMethod<IExceptionHandlerInfo>("set_ReturnValue"); IL.Emit(OpCodes.Ldloc, _exceptionInfo); IL.Emit(OpCodes.Ldloc, _returnValue); IL.Emit(OpCodes.Callvirt, setReturnValue); }
/// <summary> /// Generates a method body for the <paramref name="targetMethod"/>. /// </summary> /// <param name="originalMethod">The method currently being intercepted.</param> /// <param name="targetMethod">The target method that will contain the new method body.</param> public void Emit(MethodInfo originalMethod, MethodDefinition targetMethod) { var invocationInfo = targetMethod.AddLocal<IInvocationInfo>(); invocationInfo.Name = "___invocationInfo___"; // Emit the code to generate the IInvocationInfo instance // and save it into the invocationInfo local variable if (InvocationInfoEmitter != null) InvocationInfoEmitter.Emit(originalMethod, targetMethod, invocationInfo); var declaringType = targetMethod.DeclaringType; var module = declaringType.Module; var proxyType = module.ImportType<IProxy>(); var getInterceptorMethod = module.ImportMethod("get_Interceptor", typeof (IProxy)); var interceptor = targetMethod.AddLocal<IInterceptor>(); var arguments = targetMethod.AddLocal<object[]>(); // if (!(this is IProxy)) var IL = targetMethod.GetILGenerator(); IL.Emit(OpCodes.Ldarg_0); IL.Emit(OpCodes.Isinst, proxyType); var noImplementationFound = IL.Create(OpCodes.Nop); IL.Emit(OpCodes.Brfalse, noImplementationFound); var endLabel = IL.Create(OpCodes.Nop); EmitGetInterceptorInstruction(IL, proxyType, getInterceptorMethod); IL.Emit(OpCodes.Stloc, interceptor); //If (interceptor == null) // throw a not implemented exception here IL.Emit(OpCodes.Ldloc, interceptor); IL.Emit(OpCodes.Brfalse, noImplementationFound); // var returnValue = interceptor.Intercept(info); var voidType = module.ImportType(typeof (void)); var interceptMethod = module.ImportMethod<IInterceptor>("Intercept", typeof (IInvocationInfo)); IL.Emit(OpCodes.Ldloc, interceptor); IL.Emit(OpCodes.Ldloc, invocationInfo); IL.Emit(OpCodes.Callvirt, interceptMethod); // Save the ref arguments var parameters = from ParameterDefinition param in targetMethod.Parameters select param; // Determine the return type var returnType = targetMethod.ReturnType != null ? targetMethod.ReturnType.ReturnType : voidType; IL.PackageReturnValue(module, returnType); SaveRefArguments(IL, parameters, invocationInfo, arguments); IL.Emit(OpCodes.Br, endLabel); // This code at this point will execute if no implementation // is found IL.Append(noImplementationFound); ImplementNotFound(IL); IL.Append(endLabel); IL.Emit(OpCodes.Ret); }
/// <summary> /// Defines the instructions that will instantiate the singleton instance itself. /// </summary> /// <param name="dependency">The dependency that will be instantiated by the singleton.</param> /// <param name="implementation">The implementation that will instantiate the dependency.</param> /// <param name="serviceMap">The service map that contains the list of dependencies in the application.</param> /// <param name="instanceField">The field that will hold the singleton instance.</param> /// <param name="cctor">The static constructor itself.</param> /// <param name="module">The target module.</param> /// <param name="targetMethod">The target method that will instantiate the service instance.</param> protected virtual void EmitSingletonInstantiation(IDependency dependency, IImplementation implementation, IDictionary<IDependency, IImplementation> serviceMap, FieldDefinition instanceField, MethodDefinition cctor, ModuleDefinition module, MethodDefinition targetMethod) { var worker = cctor.GetILGenerator(); implementation.Emit(dependency, serviceMap, cctor); worker.Emit(OpCodes.Stsfld, instanceField); worker.Emit(OpCodes.Ret); }
/// <summary> /// Emits the instructions that will instantiate the current implementation. /// </summary> /// <param name="dependency">The dependency that describes the service to be instantiated.</param> /// <param name="serviceMap">The service map that contains the list of dependencies in the application.</param> /// <param name="targetMethod">The target method.</param> public void Emit(IDependency dependency, IDictionary<IDependency, IImplementation> serviceMap, MethodDefinition targetMethod) { _emitter.EmitService(targetMethod, dependency, _implementation, serviceMap); var il = targetMethod.GetILGenerator(); var module = targetMethod.Module; var serviceType = module.Import(dependency.ServiceType); if (serviceType.IsValueType) return; il.Emit(OpCodes.Castclass, serviceType); }