public static IEnumerable <Instruction> PushConvertedValue(this ValueNode node, ILContext context, TypeReference targetTypeRef, TypeReference typeConverter, IEnumerable <Instruction> pushServiceProvider, bool boxValueTypes, bool unboxValueTypes) { var module = context.Body.Method.Module; var str = (string)node.Value; //If the TypeConverter has a ProvideCompiledAttribute that can be resolved, shortcut this Type compiledConverterType; if (typeConverter?.GetCustomAttribute(module, ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls.Xaml", "ProvideCompiledAttribute"))?.ConstructorArguments?.First().Value is string compiledConverterName && (compiledConverterType = Type.GetType(compiledConverterName)) != null) { var compiledConverter = Activator.CreateInstance(compiledConverterType); var converter = typeof(ICompiledTypeConverter).GetMethods().FirstOrDefault(md => md.Name == "ConvertFromString"); IEnumerable <Instruction> instructions; try { instructions = (IEnumerable <Instruction>)converter.Invoke(compiledConverter, new object[] { node.Value as string, context, node as BaseNode }); } catch (System.Reflection.TargetInvocationException tie) when(tie.InnerException is XamlParseException) { throw tie.InnerException; } catch (System.Reflection.TargetInvocationException tie) when(tie.InnerException is BuildException) { throw tie.InnerException; } foreach (var i in instructions) { yield return(i); } if (targetTypeRef.IsValueType && boxValueTypes) { yield return(Instruction.Create(OpCodes.Box, module.ImportReference(targetTypeRef))); } yield break; } //If there's a [TypeConverter], use it if (typeConverter != null) { var isExtendedConverter = typeConverter.ImplementsInterface(module.ImportReference(("Microsoft.Maui.Controls", "Microsoft.Maui.Controls", "IExtendedTypeConverter"))); var typeConverterCtorRef = module.ImportCtorReference(typeConverter, paramCount: 0); var convertFromInvariantStringDefinition = isExtendedConverter ? module.ImportReference(("Microsoft.Maui.Controls", "Microsoft.Maui.Controls", "IExtendedTypeConverter")) .ResolveCached() .Methods.FirstOrDefault(md => md.Name == "ConvertFromInvariantString" && md.Parameters.Count == 2) : typeConverter.ResolveCached() .AllMethods() .FirstOrDefault(md => md.methodDef.Name == "ConvertFromInvariantString" && md.methodDef.Parameters.Count == 1).methodDef; var convertFromInvariantStringReference = module.ImportReference(convertFromInvariantStringDefinition); yield return(Create(Newobj, typeConverterCtorRef)); yield return(Create(Ldstr, node.Value as string)); if (isExtendedConverter) { foreach (var instruction in pushServiceProvider) { yield return(instruction); } } yield return(Instruction.Create(OpCodes.Callvirt, convertFromInvariantStringReference)); if (targetTypeRef.IsValueType && unboxValueTypes) { yield return(Instruction.Create(OpCodes.Unbox_Any, module.ImportReference(targetTypeRef))); } //ConvertFrom returns an object, no need to Box yield break; } var originalTypeRef = targetTypeRef; var isNullable = false; MethodReference nullableCtor = null; if (targetTypeRef.ResolveCached().FullName == "System.Nullable`1") { var nullableTypeRef = targetTypeRef; targetTypeRef = ((GenericInstanceType)targetTypeRef).GenericArguments[0]; isNullable = true; nullableCtor = originalTypeRef.GetMethods(md => md.IsConstructor && md.Parameters.Count == 1, module).Single().Item1; nullableCtor = nullableCtor.ResolveGenericParameters(nullableTypeRef, module); } var implicitOperator = module.TypeSystem.String.GetImplicitOperatorTo(targetTypeRef, module); //Obvious Built-in conversions if (targetTypeRef.ResolveCached().BaseType != null && targetTypeRef.ResolveCached().BaseType.FullName == "System.Enum") { yield return(PushParsedEnum(targetTypeRef, str, node)); } else if (targetTypeRef.FullName == "System.Char") { yield return(Instruction.Create(OpCodes.Ldc_I4, unchecked ((int)TryFormat(Char.Parse, node, str)))); } else if (targetTypeRef.FullName == "System.SByte") { yield return(Instruction.Create(OpCodes.Ldc_I4, unchecked ((int)TryFormat(s => SByte.Parse(s, CultureInfo.InvariantCulture), node, str)))); } else if (targetTypeRef.FullName == "System.Int16") { yield return(Instruction.Create(OpCodes.Ldc_I4, unchecked ((int)TryFormat(s => Int16.Parse(s, CultureInfo.InvariantCulture), node, str)))); } else if (targetTypeRef.FullName == "System.Int32") { yield return(Instruction.Create(OpCodes.Ldc_I4, TryFormat(s => Int32.Parse(s, CultureInfo.InvariantCulture), node, str))); } else if (targetTypeRef.FullName == "System.Int64") { yield return(Instruction.Create(OpCodes.Ldc_I8, TryFormat(s => Int64.Parse(s, CultureInfo.InvariantCulture), node, str))); } else if (targetTypeRef.FullName == "System.Byte") { yield return(Instruction.Create(OpCodes.Ldc_I4, unchecked ((int)TryFormat(s => Byte.Parse(s, CultureInfo.InvariantCulture), node, str)))); } else if (targetTypeRef.FullName == "System.UInt16") { yield return(Instruction.Create(OpCodes.Ldc_I4, unchecked ((int)TryFormat(s => UInt16.Parse(s, CultureInfo.InvariantCulture), node, str)))); } else if (targetTypeRef.FullName == "System.UInt32") { yield return(Instruction.Create(OpCodes.Ldc_I4, unchecked ((int)TryFormat(s => UInt32.Parse(s, CultureInfo.InvariantCulture), node, str)))); } else if (targetTypeRef.FullName == "System.UInt64") { yield return(Instruction.Create(OpCodes.Ldc_I8, unchecked ((long)TryFormat(s => UInt64.Parse(s, CultureInfo.InvariantCulture), node, str)))); } else if (targetTypeRef.FullName == "System.Single") { yield return(Instruction.Create(OpCodes.Ldc_R4, TryFormat(s => Single.Parse(str, CultureInfo.InvariantCulture), node, str))); } else if (targetTypeRef.FullName == "System.Double") { yield return(Instruction.Create(OpCodes.Ldc_R8, TryFormat(s => Double.Parse(str, CultureInfo.InvariantCulture), node, str))); } else if (targetTypeRef.FullName == "System.Boolean") { if (TryFormat(Boolean.Parse, node, str)) { yield return(Instruction.Create(OpCodes.Ldc_I4_1)); } else { yield return(Instruction.Create(OpCodes.Ldc_I4_0)); } } else if (targetTypeRef.FullName == "System.TimeSpan") { var ts = TryFormat(s => TimeSpan.Parse(s, CultureInfo.InvariantCulture), node, str); var ticks = ts.Ticks; yield return(Instruction.Create(OpCodes.Ldc_I8, ticks)); yield return(Instruction.Create(OpCodes.Newobj, module.ImportCtorReference(("mscorlib", "System", "TimeSpan"), parameterTypes: new[] { ("mscorlib", "System", "Int64") })));
public static IEnumerable <Instruction> PushConvertedValue(this ValueNode node, ILContext context, TypeReference targetTypeRef, TypeReference typeConverter, IEnumerable <Instruction> pushServiceProvider, bool boxValueTypes, bool unboxValueTypes) { var module = context.Body.Method.Module; var str = (string)node.Value; //If the TypeConverter has a ProvideCompiledAttribute that can be resolved, shortcut this var compiledConverterName = typeConverter?.GetCustomAttribute(module, ("Xamarin.Forms.Core", "Xamarin.Forms.Xaml", "ProvideCompiledAttribute"))?.ConstructorArguments?.First().Value as string; Type compiledConverterType; if (compiledConverterName != null && (compiledConverterType = Type.GetType(compiledConverterName)) != null) { var compiledConverter = Activator.CreateInstance(compiledConverterType); var converter = typeof(ICompiledTypeConverter).GetMethods().FirstOrDefault(md => md.Name == "ConvertFromString"); var instructions = (IEnumerable <Instruction>)converter.Invoke(compiledConverter, new object[] { node.Value as string, context, node as BaseNode }); foreach (var i in instructions) { yield return(i); } if (targetTypeRef.IsValueType && boxValueTypes) { yield return(Instruction.Create(OpCodes.Box, module.ImportReference(targetTypeRef))); } yield break; } //If there's a [TypeConverter], use it if (typeConverter != null) { var isExtendedConverter = typeConverter.ImplementsInterface(module.ImportReference(("Xamarin.Forms.Core", "Xamarin.Forms", "IExtendedTypeConverter"))); var typeConverterCtorRef = module.ImportCtorReference(typeConverter, paramCount: 0, predicate: md => !md.IsStatic); var convertFromInvariantStringDefinition = isExtendedConverter ? module.ImportReference(("Xamarin.Forms.Core", "Xamarin.Forms", "IExtendedTypeConverter")) .ResolveCached() .Methods.FirstOrDefault(md => md.Name == "ConvertFromInvariantString" && md.Parameters.Count == 2) : typeConverter.ResolveCached() .AllMethods() .FirstOrDefault(md => md.Name == "ConvertFromInvariantString" && md.Parameters.Count == 1); var convertFromInvariantStringReference = module.ImportReference(convertFromInvariantStringDefinition); yield return(Instruction.Create(OpCodes.Newobj, typeConverterCtorRef)); yield return(Instruction.Create(OpCodes.Ldstr, node.Value as string)); if (isExtendedConverter) { foreach (var instruction in pushServiceProvider) { yield return(instruction); } } yield return(Instruction.Create(OpCodes.Callvirt, convertFromInvariantStringReference)); if (targetTypeRef.IsValueType && unboxValueTypes) { yield return(Instruction.Create(OpCodes.Unbox_Any, module.ImportReference(targetTypeRef))); } //ConvertFrom returns an object, no need to Box yield break; } var originalTypeRef = targetTypeRef; var isNullable = false; MethodReference nullableCtor = null; if (targetTypeRef.ResolveCached().FullName == "System.Nullable`1") { var nullableTypeRef = targetTypeRef; targetTypeRef = ((GenericInstanceType)targetTypeRef).GenericArguments[0]; isNullable = true; nullableCtor = originalTypeRef.GetMethods(md => md.IsConstructor && md.Parameters.Count == 1, module).Single().Item1; nullableCtor = nullableCtor.ResolveGenericParameters(nullableTypeRef, module); } var implicitOperator = module.TypeSystem.String.GetImplicitOperatorTo(targetTypeRef, module); //Obvious Built-in conversions if (targetTypeRef.ResolveCached().BaseType != null && targetTypeRef.ResolveCached().BaseType.FullName == "System.Enum") { yield return(PushParsedEnum(targetTypeRef, str, node)); } else if (targetTypeRef.FullName == "System.Char") { yield return(Instruction.Create(OpCodes.Ldc_I4, Char.Parse(str))); } else if (targetTypeRef.FullName == "System.SByte") { yield return(Instruction.Create(OpCodes.Ldc_I4, SByte.Parse(str, CultureInfo.InvariantCulture))); } else if (targetTypeRef.FullName == "System.Int16") { yield return(Instruction.Create(OpCodes.Ldc_I4, Int16.Parse(str, CultureInfo.InvariantCulture))); } else if (targetTypeRef.FullName == "System.Int32") { yield return(Instruction.Create(OpCodes.Ldc_I4, Int32.Parse(str, CultureInfo.InvariantCulture))); } else if (targetTypeRef.FullName == "System.Int64") { yield return(Instruction.Create(OpCodes.Ldc_I8, Int64.Parse(str, CultureInfo.InvariantCulture))); } else if (targetTypeRef.FullName == "System.Byte") { yield return(Instruction.Create(OpCodes.Ldc_I4, Byte.Parse(str, CultureInfo.InvariantCulture))); } else if (targetTypeRef.FullName == "System.UInt16") { yield return(Instruction.Create(OpCodes.Ldc_I4, unchecked ((int)UInt16.Parse(str, CultureInfo.InvariantCulture)))); } else if (targetTypeRef.FullName == "System.UInt32") { yield return(Instruction.Create(OpCodes.Ldc_I4, UInt32.Parse(str, CultureInfo.InvariantCulture))); } else if (targetTypeRef.FullName == "System.UInt64") { yield return(Instruction.Create(OpCodes.Ldc_I8, unchecked ((long)UInt64.Parse(str, CultureInfo.InvariantCulture)))); } else if (targetTypeRef.FullName == "System.Single") { yield return(Instruction.Create(OpCodes.Ldc_R4, Single.Parse(str, CultureInfo.InvariantCulture))); } else if (targetTypeRef.FullName == "System.Double") { yield return(Instruction.Create(OpCodes.Ldc_R8, Double.Parse(str, CultureInfo.InvariantCulture))); } else if (targetTypeRef.FullName == "System.Boolean") { if (Boolean.Parse(str)) { yield return(Instruction.Create(OpCodes.Ldc_I4_1)); } else { yield return(Instruction.Create(OpCodes.Ldc_I4_0)); } } else if (targetTypeRef.FullName == "System.TimeSpan") { var ts = TimeSpan.Parse(str, CultureInfo.InvariantCulture); var ticks = ts.Ticks; var timeSpanCtorRef = module.ImportCtorReference(("mscorlib", "System", "TimeSpan"), paramCount: 1); yield return(Instruction.Create(OpCodes.Ldc_I8, ticks)); yield return(Instruction.Create(OpCodes.Newobj, timeSpanCtorRef)); } else if (targetTypeRef.FullName == "System.DateTime") { var dt = DateTime.Parse(str, CultureInfo.InvariantCulture); var ticks = dt.Ticks; var dateTimeCtorRef = module.ImportCtorReference(("mscorlib", "System", "DateTime"), paramCount: 1); yield return(Instruction.Create(OpCodes.Ldc_I8, ticks)); yield return(Instruction.Create(OpCodes.Newobj, dateTimeCtorRef)); } else if (targetTypeRef.FullName == "System.String" && str.StartsWith("{}", StringComparison.Ordinal)) { yield return(Instruction.Create(OpCodes.Ldstr, str.Substring(2))); } else if (targetTypeRef.FullName == "System.String") { yield return(Instruction.Create(OpCodes.Ldstr, str)); } else if (targetTypeRef.FullName == "System.Object") { yield return(Instruction.Create(OpCodes.Ldstr, str)); } else if (targetTypeRef.FullName == "System.Decimal") { decimal outdecimal; if (decimal.TryParse(str, NumberStyles.Number, CultureInfo.InvariantCulture, out outdecimal)) { var vardef = new VariableDefinition(module.ImportReference(("mscorlib", "System", "Decimal"))); context.Body.Variables.Add(vardef); //Use an extra temp var so we can push the value to the stack, just like other cases // IL_0003: ldstr "adecimal" // IL_0008: ldc.i4.s 0x6f // IL_000a: call class [mscorlib]System.Globalization.CultureInfo class [mscorlib]System.Globalization.CultureInfo::get_InvariantCulture() // IL_000f: ldloca.s 0 // IL_0011: call bool valuetype [mscorlib]System.Decimal::TryParse(string, valuetype [mscorlib]System.Globalization.NumberStyles, class [mscorlib]System.IFormatProvider, [out] valuetype [mscorlib]System.Decimal&) // IL_0016: pop yield return(Instruction.Create(OpCodes.Ldstr, str)); yield return(Instruction.Create(OpCodes.Ldc_I4, 0x6f)); //NumberStyles.Number var getInvariant = module.ImportPropertyGetterReference(("mscorlib", "System.Globalization", "CultureInfo"), propertyName: "InvariantCulture"); yield return(Instruction.Create(OpCodes.Call, getInvariant)); yield return(Instruction.Create(OpCodes.Ldloca, vardef)); var tryParse = module.ImportMethodReference(("mscorlib", "System", "Decimal"), methodName: "TryParse", paramCount: 4); yield return(Instruction.Create(OpCodes.Call, tryParse)); yield return(Instruction.Create(OpCodes.Pop)); yield return(Instruction.Create(OpCodes.Ldloc, vardef)); } else { yield return(Instruction.Create(OpCodes.Ldc_I4_0)); var decimalctor = module.ImportCtorReference(("mscorlib", "System", "Decimal"), paramCount: 1, predicate: md => md.Parameters[0].ParameterType.FullName == "System.Int32"); yield return(Instruction.Create(OpCodes.Newobj, decimalctor)); } } else if (implicitOperator != null) { yield return(Instruction.Create(OpCodes.Ldstr, node.Value as string)); yield return(Instruction.Create(OpCodes.Call, module.ImportReference(implicitOperator))); } else { yield return(Instruction.Create(OpCodes.Ldnull)); } if (isNullable) { yield return(Instruction.Create(OpCodes.Newobj, module.ImportReference(nullableCtor))); } if (originalTypeRef.IsValueType && boxValueTypes) { yield return(Instruction.Create(OpCodes.Box, module.ImportReference(originalTypeRef))); } }