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; }
/// 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; }