Exemplo n.º 1
0
        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") })));
Exemplo n.º 2
0
        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)));
            }
        }