private CachingResult LoadStandardDictionaryFast(ILGenerator ilGenerator, IDictionary<string, object> dictionary) { Assumes.NotNull(ilGenerator); Assumes.NotNull(dictionary); Assumes.IsTrue(dictionary.Count < GenerationServices.StandardDictionaryGeneratorsCount); CachingResult result = CachingResult.SucceededResult; MethodInfo standardDictionaryGenerator = this._standardDictionaryGenerators[dictionary.Count]; // all we need to do is load all keys and values on stack and then invoke the standard generator foreach (KeyValuePair<string, object> dictionaryItem in dictionary) { // load key - boxing is never required for strings result = result.MergeResult(this.LoadValue(ilGenerator, dictionaryItem.Key)); // load value result = result.MergeResult(this.LoadValue(ilGenerator, dictionaryItem.Value)); if (GenerationServices.IsBoxingRequiredForValue(dictionaryItem.Value)) { ilGenerator.Emit(OpCodes.Box, dictionaryItem.Value.GetType()); } } // call the standard dictionary generator - this would load the value on stack ilGenerator.EmitCall(OpCodes.Call, standardDictionaryGenerator, null); return result; }
private static bool TryGetDictionaryElementType(Type type, out Type keyType, out Type valueType) { Assumes.NotNull(type); keyType = null; valueType = null; if (!type.IsGenericType) { return false; } Type genericType = type.GetGenericTypeDefinition(); Assumes.NotNull(genericType); Type closedInterfaceType = type.GetInterface(GenerationServices.GenericIDictionaryType.Name, false); if (closedInterfaceType == null) { return false; } keyType = closedInterfaceType.GetGenericArguments()[0]; valueType = closedInterfaceType.GetGenericArguments()[1]; return GenerationServices.IsLoadable(keyType) && GenerationServices.IsLoadable(valueType); }
private static bool IsLoadableDictionaryType(Type type) { Assumes.NotNull(type); Type keyType = null; Type valueType = null; return GenerationServices.TryGetDictionaryElementType(type, out keyType, out valueType); }
public ComposablePartCatalogAssemblyCacheWriter(AssemblyName assemblyName, string cacheDirectory) { Requires.NotNull(assemblyName, "assemblyName"); Requires.NotNullOrEmpty(assemblyName.Name, "assemblyName.Name"); Requires.NotNullOrEmpty(cacheDirectory, "cacheDirectory"); this._assemblyCacheFileName = assemblyName.Name + ".dll"; this._assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Save, cacheDirectory); this._moduleBuilder = this._assemblyBuilder.DefineDynamicModule(assemblyName.Name, this._assemblyCacheFileName); this._generationServices = new GenerationServices(this._moduleBuilder); }
private static MethodInfo CreateStandardDictionaryGenerator(TypeBuilder standardDictionaryGeneratorStubTypeBuilder, ushort capacity) { Assumes.NotNull(standardDictionaryGeneratorStubTypeBuilder); // static internal CreateStandardDictionaryGenerator(string key0, object value0, ... string keyN-1, object valyeN-1) Type[] argumentTypes = new Type[capacity * 2]; for (ushort i = 0; i < capacity; i++) { argumentTypes[i * 2] = GenerationServices.StringType; argumentTypes[i * 2 + 1] = GenerationServices.ObjectType; } MethodBuilder standardDictionaryGeneratorBuilder = standardDictionaryGeneratorStubTypeBuilder.DefineMethod( capacity.ToString(CultureInfo.InvariantCulture), MethodAttributes.Static | MethodAttributes.Assembly, GenerationServices.standardDictionaryType, argumentTypes); ILGenerator ilGenerator = standardDictionaryGeneratorBuilder.GetILGenerator(); // // Dictionary<string, object> metadata = new Dictionary<string, TValue>(capacity) // // create and load the dictionary GenerationServices.LoadInt(ilGenerator, capacity); ilGenerator.Emit(OpCodes.Newobj, GenerationServices.standardDictionaryConstructor); // // dictionary.Add(key0, value0) // dictionary.Add(key1, value1) // ... // for (ushort i = 0; i < capacity; i++) { // the dictionary is on top of the stack - load it again ilGenerator.Emit(OpCodes.Dup); // load the key ilGenerator.Emit(OpCodes.Ldarg, i * 2); // load the value ilGenerator.Emit(OpCodes.Ldarg, i * 2 + 1); // Caal the "Add" ilGenerator.EmitCall(OpCodes.Call, GenerationServices.standardDictionaryAddMethod, null); } ilGenerator.Emit(OpCodes.Ret); return standardDictionaryGeneratorBuilder; }
private static CachingResult LoadString(ILGenerator ilGenerator, string s) { Assumes.NotNull(ilGenerator); if (s == null) { return GenerationServices.LoadNull(ilGenerator); } else { ilGenerator.Emit(OpCodes.Ldstr, s); } return CachingResult.SucceededResult; }
internal static bool IsLoadable(object value) { Type valueType = (value != null) ? value.GetType() : null; return ( (valueType == null) || (valueType.IsPrimitive) || valueType.IsEnum || (value is string) || (value is Type)|| GenerationServices.IsLoadableDictionaryType(valueType) || (value is IEnumerable) ); }
public AssemblyCacheGenerator(ModuleBuilder moduleBuilder, GenerationServices generationServices, ICachedComposablePartCatalogSite cachedCatalogSite, string catalogIdentifier) { Assumes.NotNull(moduleBuilder); Assumes.NotNull(generationServices); Assumes.NotNull(cachedCatalogSite); Assumes.NotNull(catalogIdentifier); this._moduleBuilder = moduleBuilder; this._generationServices = generationServices; this._catalogIdentifier = catalogIdentifier ?? string.Empty; this._catalogMetadata = new Dictionary <string, object>(); this._catalogIndex = new Dictionary <string, List <MethodInfo> >(); this._cachedCatalogSite = cachedCatalogSite; }
private CachingResult LoadEnumerable(ILGenerator ilGenerator, IEnumerable enumerable) { Assumes.NotNull(ilGenerator); Assumes.NotNull(enumerable); CachingResult result = CachingResult.SucceededResult; // We load enumerable as an array - this is the most compact and efficient way of representing it Type elementType = null; Type closedType = null; if (GenerationServices.TryGetGenericInterfaceType(enumerable.GetType(), GenerationServices.IEnumerableTypeofT, out closedType)) { elementType = closedType.GetGenericArguments()[0]; } else { elementType = typeof(object); } elementType = GenerationServices.NormalizeCollectionElementType(elementType); // // elem[] array = new elem[<enumerable.Count()>] // GenerationServices.LoadInt(ilGenerator, enumerable.Cast<object>().Count()); ilGenerator.Emit(OpCodes.Newarr, elementType); // at this point we have the array on the stack int index = 0; foreach (object value in enumerable) { // //array[<index>] = value; // // load the array on teh stack again ilGenerator.Emit(OpCodes.Dup); GenerationServices.LoadInt(ilGenerator, index); result = result.MergeResult(this.LoadValue(ilGenerator, value)); if (GenerationServices.IsBoxingRequiredForValue(value) && !elementType.IsValueType) { ilGenerator.Emit(OpCodes.Box, value.GetType()); } ilGenerator.Emit(OpCodes.Stelem, elementType); index++; // at this point we have the array on teh stack again } // the array is already on the stack - just exit return result; }
private static MethodInfo[] CreateStandardDictionaryGenerators(ModuleBuilder moduleBuilder) { Assumes.NotNull(moduleBuilder); TypeBuilder standardDictionaryGeneratorStubTypeBuilder = moduleBuilder.DefineType( GenerationServices.StandardDictionaryGeneratorStubName, TypeAttributes.NotPublic | TypeAttributes.Abstract); MethodInfo[] standardDictionaryGenerators = new MethodInfo[GenerationServices.StandardDictionaryGeneratorsCount]; for (ushort i = 0; i < GenerationServices.StandardDictionaryGeneratorsCount; i++) { standardDictionaryGenerators[i] = GenerationServices.CreateStandardDictionaryGenerator(standardDictionaryGeneratorStubTypeBuilder, i); } standardDictionaryGeneratorStubTypeBuilder.CreateType(); return standardDictionaryGenerators; }
private static Type NormalizeCollectionElementType(Type type) { if (GenerationServices.IEnumerableType.IsAssignableFrom(type) && type != GenerationServices.StringType) { // the element is IEnumerable. we need to normalize it to be literally IEnumerable Type closedType = null; if (GenerationServices.TryGetGenericInterfaceType(type, GenerationServices.IEnumerableTypeofT, out closedType)) { return GenerationServices.IEnumerableTypeofT.MakeGenericType(GenerationServices.NormalizeCollectionElementType(closedType.GetGenericArguments()[0])); } else { return typeof(IEnumerable<object>); } } else { return type; } }
private static MethodInfo[] GetStandardDictionaryGenerators(ModuleBuilder moduleBuilder) { Assumes.NotNull(moduleBuilder); Type standardDictionaryGeneratorStubType = moduleBuilder.GetType(GenerationServices.StandardDictionaryGeneratorStubName); if (standardDictionaryGeneratorStubType == null) { return GenerationServices.CreateStandardDictionaryGenerators(moduleBuilder); } else { MethodInfo[] standardDictionaryGenerators = new MethodInfo[GenerationServices.StandardDictionaryGeneratorsCount]; MethodInfo[] standardDictionaryGeneratorsUnordered = standardDictionaryGeneratorStubType.GetMethods(BindingFlags.NonPublic | BindingFlags.Static); Assumes.IsTrue(standardDictionaryGeneratorsUnordered.Length == GenerationServices.StandardDictionaryGeneratorsCount); foreach (MethodInfo standardDictionaryGenerator in standardDictionaryGeneratorsUnordered) { int capacity = standardDictionaryGenerator.GetParameters().Length / 2; standardDictionaryGenerators[capacity] = standardDictionaryGenerator; } return standardDictionaryGenerators; } }
/// Generates the code that loads the supplied value on the stack /// This is not as simple as it seems, as different instructions need to be generated depending /// on its type. /// We support: /// 1. All primitive types and IntPtrs /// 2. Strings /// 3. Enums /// 4. typeofs /// 5. nulls /// 6. Dictionaries of (string, object) recursively containing all of the above /// 7. Enumerables /// Everything else cannot be represented as literals /// <param name="ilGenerator"></param> /// <param name="item"></param> /// <param name="key"></param> /// <param name="value"></param> /// <returns></returns> internal CachingResult LoadValue(ILGenerator ilGenerator, object value) { Assumes.NotNull(ilGenerator); CachingResult result = CachingResult.SucceededResult; // // Get nulls out of the way - they are basically typeless, so we just load null // if (value == null) { return GenerationServices.LoadNull(ilGenerator); } // // Prepare for literal loading - decide whether we should box, and handle enums properly // Type valueType = value.GetType(); object rawValue = value; if (valueType.IsEnum) { // enums are special - we need to load the underlying constant on the stack rawValue = Convert.ChangeType(value, Enum.GetUnderlyingType(valueType), null); valueType = rawValue.GetType(); } // // Generate IL depending on the valueType - this is messier than it should ever be, but sadly necessary // Type dictionaryKeyType; Type dictionaryValueType; IDictionary<string, object> standardDictionary = value as IDictionary<string, object>; if (standardDictionary != null) { if (standardDictionary.Count < GenerationServices.StandardDictionaryGeneratorsCount) { return this.LoadStandardDictionaryFast(ilGenerator, standardDictionary); } else { return this.LoadGenericDictionary(ilGenerator, standardDictionary, true); } } else if (GenerationServices.TryGetDictionaryElementType(valueType, out dictionaryKeyType, out dictionaryValueType)) { result = result.MergeResult(this.LoadDictionary(ilGenerator, rawValue, dictionaryKeyType, dictionaryValueType)); } else if (valueType == GenerationServices.StringType) { // we need to check for strings before enumerables, because strings are IEnumerable<char> result = result.MergeResult(GenerationServices.LoadString(ilGenerator,(string)rawValue)); } else if (GenerationServices.TypeType.IsAssignableFrom(valueType)) { result = result.MergeResult(GenerationServices.LoadTypeOf(ilGenerator, (Type)rawValue)); } else if (GenerationServices.IEnumerableType.IsAssignableFrom(valueType)) { // NOTE : strings and dictionaries are also enumerables, but we have already handled those result = result.MergeResult(this.LoadEnumerable(ilGenerator, (IEnumerable) rawValue)); } else if ( (valueType == GenerationServices.CharType) || (valueType == GenerationServices.BooleanType) || (valueType == GenerationServices.ByteType) || (valueType == GenerationServices.SByteType) || (valueType == GenerationServices.Int16Type) || (valueType == GenerationServices.UInt16Type) || (valueType == GenerationServices.Int32Type) ) { // NOTE : Everything that is 32 bit or less uses ldc.i4. We need to pass int32, even if the actual types is shorter - this is IL memory model // direct casting to (int) won't work, because the value is boxed, thus we need to use Convert. // Sadly, this will not work for all cases - namely large uint32 - because they can't semantically fit into 32 signed bits // We have a special case for that next result = result.MergeResult(GenerationServices.LoadInt(ilGenerator, (int)Convert.ChangeType(rawValue, typeof(int), CultureInfo.InvariantCulture))); } else if (valueType == GenerationServices.UInt32Type) { // NOTE : This one is a bit tricky. Ldc.I4 takes an Int32 as an argument, although it really treats it as a 32bit number // That said, some UInt32 values are larger that Int32.MaxValue, so the Convert call above will fail, which is why // we need to treat this case individually and cast to uint, and then - unchecked - to int. result = result.MergeResult(GenerationServices.LoadInt(ilGenerator, unchecked((int)((uint)rawValue)))); } else if (valueType == GenerationServices.Int64Type) { result = result.MergeResult(GenerationServices.LoadLong(ilGenerator, (long)rawValue)); } else if (valueType == GenerationServices.UInt64Type) { // NOTE : This one is a bit tricky. Ldc.I8 takes an Int64 as an argument, although it really treats it as a 64bit number // That said, some UInt64 values are larger that Int64.MaxValue, so the direct case we use above (or Convert, for that matter)will fail, which is why // we need to treat this case individually and cast to ulong, and then - unchecked - to long. result = result.MergeResult(GenerationServices.LoadLong(ilGenerator, unchecked((long)((ulong)rawValue)))); } else if (valueType == GenerationServices.SingleType) { result = result.MergeResult(GenerationServices.LoadFloat(ilGenerator, (float)rawValue)); } else if (valueType == GenerationServices.DoubleType) { result = result.MergeResult(GenerationServices.LoadDouble(ilGenerator, (double)rawValue)); } else { result = result.MergeError(Strings.UnsupportedCacheValue, value.GetType().FullName); // Make sure the IL is balanced - generate the ldnull instead GenerationServices.LoadNull(ilGenerator); } return result; }
public GenerationServices(ModuleBuilder moduleBuilder) { Assumes.NotNull(moduleBuilder); this._standardDictionaryGenerators = GenerationServices.GetStandardDictionaryGenerators(moduleBuilder); }
private CachingResult LoadGenericDictionary<TKey, TValue>(ILGenerator ilGenerator, IDictionary<TKey, TValue> dictionary, bool isStandardDictionary) { Assumes.NotNull(ilGenerator); Assumes.NotNull(dictionary); CachingResult result = CachingResult.SucceededResult; Type keyType = GenerationServices.NormalizeCollectionElementType(typeof(TKey)); Type valueType = GenerationServices.NormalizeCollectionElementType(typeof(TValue)); Type dictionaryType = null; MethodInfo dictionaryAddMethod = null; ConstructorInfo dictionaryConstructor = null; if (isStandardDictionary) { dictionaryType = GenerationServices.standardDictionaryType; dictionaryAddMethod = GenerationServices.standardDictionaryAddMethod; dictionaryConstructor = dictionaryType.GetConstructor(new Type[] { Int32Type }); } else { dictionaryType = GenerationServices.GenericDictionaryType.MakeGenericType(keyType, valueType); dictionaryAddMethod = dictionaryType.GetMethod("Add", new Type[] { keyType, valueType }); dictionaryConstructor = dictionaryType.GetConstructor(new Type[] { Int32Type }); } // // Dictionary<TKey, TValue> metadata = new Dictionary<TKey, TValue>(capacity) // // create and load the dictionary GenerationServices.LoadInt(ilGenerator, dictionary.Count); ilGenerator.Emit(OpCodes.Newobj, dictionaryConstructor); // // Generate a sequence of "Add" statements // foreach (KeyValuePair<TKey, TValue> dictionaryItem in dictionary) { // // metadata.Add(key, value) // // the dictionary is on top of the stack - load it again ilGenerator.Emit(OpCodes.Dup); // load the key, boxing if necessary result = result.MergeResult(this.LoadValue(ilGenerator, dictionaryItem.Key)); // key = string for standard dictionaries, so no boxing is ever required if (!isStandardDictionary && GenerationServices.IsBoxingRequiredForValue(dictionaryItem.Key) && !keyType.IsValueType) { ilGenerator.Emit(OpCodes.Box, dictionaryItem.Key.GetType()); } // load the value, boxing if necessary result = result.MergeResult(this.LoadValue(ilGenerator, dictionaryItem.Value)); // key = object for standard dictionaries, so value type is never a struct if (GenerationServices.IsBoxingRequiredForValue(dictionaryItem.Value) && (isStandardDictionary || !valueType.IsValueType) ) { ilGenerator.Emit(OpCodes.Box, dictionaryItem.Value.GetType()); } // Caal the "Add" ilGenerator.EmitCall(OpCodes.Call, dictionaryAddMethod, null); // At this point the dictionary, key and value have been popped off the stack, and we ended up with the origical state of the dictionary on top of the stack } // // the dicationary is already loaded on the stack - exit // return result; }