diff options
-rw-r--r-- | Xamarin.Forms.Build.Tasks/CreateObjectVisitor.cs | 410 | ||||
-rw-r--r-- | Xamarin.Forms.Build.Tasks/NodeILExtensions.cs | 72 | ||||
-rw-r--r-- | Xamarin.Forms.Xaml.UnitTests/SetValue.xaml | 1 | ||||
-rw-r--r-- | Xamarin.Forms.Xaml.UnitTests/SetValue.xaml.cs | 28 | ||||
-rw-r--r-- | Xamarin.Forms.Xaml.UnitTests/X2009Primitives.xaml | 4 | ||||
-rw-r--r-- | Xamarin.Forms.Xaml.UnitTests/X2009Primitives.xaml.cs | 69 | ||||
-rw-r--r-- | Xamarin.Forms.Xaml/CreateValuesVisitor.cs | 108 | ||||
-rw-r--r-- | Xamarin.Forms.Xaml/TypeConversionExtensions.cs | 34 |
8 files changed, 446 insertions, 280 deletions
diff --git a/Xamarin.Forms.Build.Tasks/CreateObjectVisitor.cs b/Xamarin.Forms.Build.Tasks/CreateObjectVisitor.cs index 33a9a7f5..6d6405e1 100644 --- a/Xamarin.Forms.Build.Tasks/CreateObjectVisitor.cs +++ b/Xamarin.Forms.Build.Tasks/CreateObjectVisitor.cs @@ -317,18 +317,22 @@ namespace Xamarin.Forms.Build.Tasks if (node.NamespaceURI != "clr-namespace:System;assembly=mscorlib") return false; var name = node.XmlType.Name.Split(':')[1]; - if (name == "Boolean" || - name == "String" || - name == "Char" || - name == "Decimal" || - name == "Single" || - name == "Double" || - name == "Byte" || - name == "Int16" || - name == "Int32" || - name == "Int64" || - name == "TimeSpan" || - name == "Uri") + if (name == "SByte" || + name == "Int16" || + name == "Int32" || + name == "Int64" || + name == "Byte" || + name == "UInt16" || + name == "UInt32" || + name == "UInt64" || + name == "Single" || + name == "Double" || + name == "Boolean" || + name == "String" || + name == "Char" || + name == "Decimal" || + name == "TimeSpan" || + name == "Uri") return true; return false; } @@ -338,190 +342,206 @@ namespace Xamarin.Forms.Build.Tasks var hasValue = node.CollectionItems.Count == 1 && node.CollectionItems[0] is ValueNode && ((ValueNode)node.CollectionItems[0]).Value is string; var valueString = hasValue ? ((ValueNode)node.CollectionItems[0]).Value as string : string.Empty; - switch (typedef.FullName) - { - case "System.Boolean": - bool outbool; - if (hasValue && bool.TryParse(valueString, out outbool)) - yield return Instruction.Create(outbool ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); - else - yield return Instruction.Create(OpCodes.Ldc_I4_0); - break; - case "System.String": + switch (typedef.FullName) { + case "System.SByte": + sbyte outsbyte; + if (hasValue && sbyte.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out outsbyte)) + yield return Instruction.Create(OpCodes.Ldc_I4, (int)outsbyte); + else + yield return Instruction.Create(OpCodes.Ldc_I4, 0x00); + break; + case "System.Int16": + short outshort; + if (hasValue && short.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out outshort)) + yield return Instruction.Create(OpCodes.Ldc_I4, outshort); + else + yield return Instruction.Create(OpCodes.Ldc_I4, 0x00); + break; + case "System.Int32": + int outint; + if (hasValue && int.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out outint)) + yield return Instruction.Create(OpCodes.Ldc_I4, outint); + else + yield return Instruction.Create(OpCodes.Ldc_I4, 0x00); + break; + case "System.Int64": + long outlong; + if (hasValue && long.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out outlong)) + yield return Instruction.Create(OpCodes.Ldc_I8, outlong); + else + yield return Instruction.Create(OpCodes.Ldc_I8, 0L); + break; + case "System.Byte": + byte outbyte; + if (hasValue && byte.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out outbyte)) + yield return Instruction.Create(OpCodes.Ldc_I4, (int)outbyte); + else + yield return Instruction.Create(OpCodes.Ldc_I4, 0x00); + break; + case "System.UInt16": + short outushort; + if (hasValue && short.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out outushort)) + yield return Instruction.Create(OpCodes.Ldc_I4, outushort); + else + yield return Instruction.Create(OpCodes.Ldc_I4, 0x00); + break; + case "System.UInt32": + int outuint; + if (hasValue && int.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out outuint)) + yield return Instruction.Create(OpCodes.Ldc_I4, outuint); + else + yield return Instruction.Create(OpCodes.Ldc_I4, 0x00); + break; + case "System.UInt64": + long outulong; + if (hasValue && long.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out outulong)) + yield return Instruction.Create(OpCodes.Ldc_I8, outulong); + else + yield return Instruction.Create(OpCodes.Ldc_I8, 0L); + break; + case "System.Boolean": + bool outbool; + if (hasValue && bool.TryParse(valueString, out outbool)) + yield return Instruction.Create(outbool ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); + else + yield return Instruction.Create(OpCodes.Ldc_I4_0); + break; + case "System.String": + yield return Instruction.Create(OpCodes.Ldstr, valueString); + break; + case "System.Object": + var ctorinfo = + Context.Body.Method.Module.TypeSystem.Object.Resolve() + .Methods.FirstOrDefault(md => md.IsConstructor && !md.HasParameters); + var ctor = Context.Body.Method.Module.Import(ctorinfo); + yield return Instruction.Create(OpCodes.Newobj, ctor); + break; + case "System.Char": + char outchar; + if (hasValue && char.TryParse(valueString, out outchar)) + yield return Instruction.Create(OpCodes.Ldc_I4, outchar); + else + yield return Instruction.Create(OpCodes.Ldc_I4, 0x00); + break; + case "System.Decimal": + decimal outdecimal; + if (hasValue && decimal.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out outdecimal)) { + var vardef = new VariableDefinition(Context.Body.Method.Module.Import(typeof(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, valueString); - break; - case "System.Object": - var ctorinfo = - Context.Body.Method.Module.TypeSystem.Object.Resolve() - .Methods.FirstOrDefault(md => md.IsConstructor && !md.HasParameters); - var ctor = Context.Body.Method.Module.Import(ctorinfo); - yield return Instruction.Create(OpCodes.Newobj, ctor); - break; - case "System.Char": - char outchar; - if (hasValue && char.TryParse(valueString, out outchar)) - yield return Instruction.Create(OpCodes.Ldc_I4, outchar); - else - yield return Instruction.Create(OpCodes.Ldc_I4, 0x00); - break; - case "System.Decimal": - decimal outdecimal; - if (hasValue && decimal.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out outdecimal)) - { - var vardef = new VariableDefinition(Context.Body.Method.Module.Import(typeof (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, valueString); - yield return Instruction.Create(OpCodes.Ldc_I4, 0x6f); //NumberStyles.Number - var getInvariantInfo = - Context.Body.Method.Module.Import(typeof (CultureInfo)) - .Resolve() - .Properties.FirstOrDefault(pd => pd.Name == "InvariantCulture") - .GetMethod; - var getInvariant = Context.Body.Method.Module.Import(getInvariantInfo); - yield return Instruction.Create(OpCodes.Call, getInvariant); - yield return Instruction.Create(OpCodes.Ldloca, vardef); - var tryParseInfo = - Context.Body.Method.Module.Import(typeof (decimal)) - .Resolve() - .Methods.FirstOrDefault(md => md.Name == "TryParse" && md.Parameters.Count == 4); - var tryParse = Context.Body.Method.Module.Import(tryParseInfo); - 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 decimalctorinfo = - Context.Body.Method.Module.Import(typeof (decimal)) - .Resolve() - .Methods.FirstOrDefault( - md => md.IsConstructor && md.Parameters.Count == 1 && md.Parameters[0].ParameterType.FullName == "System.Int32"); - var decimalctor = Context.Body.Method.Module.Import(decimalctorinfo); - yield return Instruction.Create(OpCodes.Newobj, decimalctor); - } - break; - case "System.Single": - float outfloat; - if (hasValue && float.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out outfloat)) - yield return Instruction.Create(OpCodes.Ldc_R4, outfloat); - else - yield return Instruction.Create(OpCodes.Ldc_R4, 0f); - break; - case "System.Double": - double outdouble; - if (hasValue && double.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out outdouble)) - yield return Instruction.Create(OpCodes.Ldc_R8, outdouble); - else - yield return Instruction.Create(OpCodes.Ldc_R8, 0d); - break; - case "System.Byte": - byte outbyte; - if (hasValue && byte.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out outbyte)) - yield return Instruction.Create(OpCodes.Ldc_I4, (int)outbyte); - else - yield return Instruction.Create(OpCodes.Ldc_I4, 0x00); - break; - case "System.Int16": - short outshort; - if (hasValue && short.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out outshort)) - yield return Instruction.Create(OpCodes.Ldc_I4, outshort); - else - yield return Instruction.Create(OpCodes.Ldc_I4, 0x00); - break; - case "System.Int32": - int outint; - if (hasValue && int.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out outint)) - yield return Instruction.Create(OpCodes.Ldc_I4, outint); - else - yield return Instruction.Create(OpCodes.Ldc_I4, 0x00); - break; - case "System.Int64": - long outlong; - if (hasValue && long.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out outlong)) - yield return Instruction.Create(OpCodes.Ldc_I8, outlong); - else - yield return Instruction.Create(OpCodes.Ldc_I8, 0L); - break; - case "System.TimeSpan": - TimeSpan outspan; - if (hasValue && TimeSpan.TryParse(valueString, CultureInfo.InvariantCulture, out outspan)) - { - var vardef = new VariableDefinition(Context.Body.Method.Module.Import(typeof (TimeSpan))); - Context.Body.Variables.Add(vardef); - //Use an extra temp var so we can push the value to the stack, just like other cases - yield return Instruction.Create(OpCodes.Ldstr, valueString); - var getInvariantInfo = - Context.Body.Method.Module.Import(typeof (CultureInfo)) - .Resolve() - .Properties.FirstOrDefault(pd => pd.Name == "InvariantCulture") - .GetMethod; - var getInvariant = Context.Body.Method.Module.Import(getInvariantInfo); - yield return Instruction.Create(OpCodes.Call, getInvariant); - yield return Instruction.Create(OpCodes.Ldloca, vardef); - var tryParseInfo = - Context.Body.Method.Module.Import(typeof (TimeSpan)) - .Resolve() - .Methods.FirstOrDefault(md => md.Name == "TryParse" && md.Parameters.Count == 3); - var tryParse = Context.Body.Method.Module.Import(tryParseInfo); - 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_I8, 0L); - var timespanctorinfo = - Context.Body.Method.Module.Import(typeof (TimeSpan)) - .Resolve() - .Methods.FirstOrDefault( - md => md.IsConstructor && md.Parameters.Count == 1 && md.Parameters[0].ParameterType.FullName == "System.Int64"); - var timespanctor = Context.Body.Method.Module.Import(timespanctorinfo); - yield return Instruction.Create(OpCodes.Newobj, timespanctor); - } - break; - case "System.Uri": - Uri outuri; - if (hasValue && Uri.TryCreate(valueString, UriKind.RelativeOrAbsolute, out outuri)) - { - var vardef = new VariableDefinition(Context.Body.Method.Module.Import(typeof (Uri))); - Context.Body.Variables.Add(vardef); - //Use an extra temp var so we can push the value to the stack, just like other cases - yield return Instruction.Create(OpCodes.Ldstr, valueString); - yield return Instruction.Create(OpCodes.Ldc_I4, (int)UriKind.RelativeOrAbsolute); - yield return Instruction.Create(OpCodes.Ldloca, vardef); - var tryCreateInfo = - Context.Body.Method.Module.Import(typeof (Uri)) - .Resolve() - .Methods.FirstOrDefault(md => md.Name == "TryCreate" && md.Parameters.Count == 3); - var tryCreate = Context.Body.Method.Module.Import(tryCreateInfo); - yield return Instruction.Create(OpCodes.Call, tryCreate); - yield return Instruction.Create(OpCodes.Pop); - yield return Instruction.Create(OpCodes.Ldloc, vardef); - } - else - yield return Instruction.Create(OpCodes.Ldnull); - break; - default: - var defaultctorinfo = typedef.Methods.FirstOrDefault(md => md.IsConstructor && !md.HasParameters); - if (defaultctorinfo != null) - { - var defaultctor = Context.Body.Method.Module.Import(defaultctorinfo); - yield return Instruction.Create(OpCodes.Newobj, defaultctor); - } - else - { - //should never happen. but if it does, this prevents corrupting the IL stack - yield return Instruction.Create(OpCodes.Ldnull); - } - break; + yield return Instruction.Create(OpCodes.Ldc_I4, 0x6f); //NumberStyles.Number + var getInvariantInfo = + Context.Body.Method.Module.Import(typeof(CultureInfo)) + .Resolve() + .Properties.FirstOrDefault(pd => pd.Name == "InvariantCulture") + .GetMethod; + var getInvariant = Context.Body.Method.Module.Import(getInvariantInfo); + yield return Instruction.Create(OpCodes.Call, getInvariant); + yield return Instruction.Create(OpCodes.Ldloca, vardef); + var tryParseInfo = + Context.Body.Method.Module.Import(typeof(decimal)) + .Resolve() + .Methods.FirstOrDefault(md => md.Name == "TryParse" && md.Parameters.Count == 4); + var tryParse = Context.Body.Method.Module.Import(tryParseInfo); + 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 decimalctorinfo = + Context.Body.Method.Module.Import(typeof(decimal)) + .Resolve() + .Methods.FirstOrDefault( + md => md.IsConstructor && md.Parameters.Count == 1 && md.Parameters [0].ParameterType.FullName == "System.Int32"); + var decimalctor = Context.Body.Method.Module.Import(decimalctorinfo); + yield return Instruction.Create(OpCodes.Newobj, decimalctor); + } + break; + case "System.Single": + float outfloat; + if (hasValue && float.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out outfloat)) + yield return Instruction.Create(OpCodes.Ldc_R4, outfloat); + else + yield return Instruction.Create(OpCodes.Ldc_R4, 0f); + break; + case "System.Double": + double outdouble; + if (hasValue && double.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out outdouble)) + yield return Instruction.Create(OpCodes.Ldc_R8, outdouble); + else + yield return Instruction.Create(OpCodes.Ldc_R8, 0d); + break; + case "System.TimeSpan": + TimeSpan outspan; + if (hasValue && TimeSpan.TryParse(valueString, CultureInfo.InvariantCulture, out outspan)) { + var vardef = new VariableDefinition(Context.Body.Method.Module.Import(typeof(TimeSpan))); + Context.Body.Variables.Add(vardef); + //Use an extra temp var so we can push the value to the stack, just like other cases + yield return Instruction.Create(OpCodes.Ldstr, valueString); + var getInvariantInfo = + Context.Body.Method.Module.Import(typeof(CultureInfo)) + .Resolve() + .Properties.FirstOrDefault(pd => pd.Name == "InvariantCulture") + .GetMethod; + var getInvariant = Context.Body.Method.Module.Import(getInvariantInfo); + yield return Instruction.Create(OpCodes.Call, getInvariant); + yield return Instruction.Create(OpCodes.Ldloca, vardef); + var tryParseInfo = + Context.Body.Method.Module.Import(typeof(TimeSpan)) + .Resolve() + .Methods.FirstOrDefault(md => md.Name == "TryParse" && md.Parameters.Count == 3); + var tryParse = Context.Body.Method.Module.Import(tryParseInfo); + 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_I8, 0L); + var timespanctorinfo = + Context.Body.Method.Module.Import(typeof(TimeSpan)) + .Resolve() + .Methods.FirstOrDefault( + md => md.IsConstructor && md.Parameters.Count == 1 && md.Parameters [0].ParameterType.FullName == "System.Int64"); + var timespanctor = Context.Body.Method.Module.Import(timespanctorinfo); + yield return Instruction.Create(OpCodes.Newobj, timespanctor); + } + break; + case "System.Uri": + Uri outuri; + if (hasValue && Uri.TryCreate(valueString, UriKind.RelativeOrAbsolute, out outuri)) { + var vardef = new VariableDefinition(Context.Body.Method.Module.Import(typeof(Uri))); + Context.Body.Variables.Add(vardef); + //Use an extra temp var so we can push the value to the stack, just like other cases + yield return Instruction.Create(OpCodes.Ldstr, valueString); + yield return Instruction.Create(OpCodes.Ldc_I4, (int)UriKind.RelativeOrAbsolute); + yield return Instruction.Create(OpCodes.Ldloca, vardef); + var tryCreateInfo = + Context.Body.Method.Module.Import(typeof(Uri)) + .Resolve() + .Methods.FirstOrDefault(md => md.Name == "TryCreate" && md.Parameters.Count == 3); + var tryCreate = Context.Body.Method.Module.Import(tryCreateInfo); + yield return Instruction.Create(OpCodes.Call, tryCreate); + yield return Instruction.Create(OpCodes.Pop); + yield return Instruction.Create(OpCodes.Ldloc, vardef); + } else + yield return Instruction.Create(OpCodes.Ldnull); + break; + default: + var defaultctorinfo = typedef.Methods.FirstOrDefault(md => md.IsConstructor && !md.HasParameters); + if (defaultctorinfo != null) { + var defaultctor = Context.Body.Method.Module.Import(defaultctorinfo); + yield return Instruction.Create(OpCodes.Newobj, defaultctor); + } else { + //should never happen. but if it does, this prevents corrupting the IL stack + yield return Instruction.Create(OpCodes.Ldnull); + } + break; } } } diff --git a/Xamarin.Forms.Build.Tasks/NodeILExtensions.cs b/Xamarin.Forms.Build.Tasks/NodeILExtensions.cs index a7fed895..31c1a1a8 100644 --- a/Xamarin.Forms.Build.Tasks/NodeILExtensions.cs +++ b/Xamarin.Forms.Build.Tasks/NodeILExtensions.cs @@ -114,58 +114,100 @@ namespace Xamarin.Forms.Build.Tasks yield return Instruction.Create(OpCodes.Ldc_I4, ParseEnum(targetTypeRef, str, node)); else if (targetTypeRef.FullName == "System.Char") yield return Instruction.Create(OpCodes.Ldc_I4, Char.Parse(str)); - else if (targetTypeRef.FullName == "System.Byte") - yield return Instruction.Create(OpCodes.Ldc_I4, Byte.Parse(str, CultureInfo.InvariantCulture)); + 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, 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, 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") - { + 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") - { + } else if (targetTypeRef.FullName == "System.TimeSpan") { var ts = TimeSpan.Parse(str, CultureInfo.InvariantCulture); var ticks = ts.Ticks; var timeSpanCtor = - module.Import(typeof (TimeSpan)) + module.Import(typeof(TimeSpan)) .Resolve() .Methods.FirstOrDefault(md => md.IsConstructor && md.Parameters.Count == 1); var timeSpanCtorRef = module.Import(timeSpanCtor); yield return Instruction.Create(OpCodes.Ldc_I8, ticks); yield return Instruction.Create(OpCodes.Newobj, timeSpanCtorRef); - } - else if (targetTypeRef.FullName == "System.DateTime") - { + } else if (targetTypeRef.FullName == "System.DateTime") { var dt = DateTime.Parse(str, CultureInfo.InvariantCulture); var ticks = dt.Ticks; var dateTimeCtor = - module.Import(typeof (DateTime)) + module.Import(typeof(DateTime)) .Resolve() .Methods.FirstOrDefault(md => md.IsConstructor && md.Parameters.Count == 1); var dateTimeCtorRef = module.Import(dateTimeCtor); 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)) + } 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 + else if (targetTypeRef.FullName == "System.Decimal") { + decimal outdecimal; + if (decimal.TryParse(str, NumberStyles.Number, CultureInfo.InvariantCulture, out outdecimal)) { + var vardef = new VariableDefinition(context.Body.Method.Module.Import(typeof(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 getInvariantInfo = + context.Body.Method.Module.Import(typeof(CultureInfo)) + .Resolve() + .Properties.FirstOrDefault(pd => pd.Name == "InvariantCulture") + .GetMethod; + var getInvariant = context.Body.Method.Module.Import(getInvariantInfo); + yield return Instruction.Create(OpCodes.Call, getInvariant); + yield return Instruction.Create(OpCodes.Ldloca, vardef); + var tryParseInfo = + context.Body.Method.Module.Import(typeof(decimal)) + .Resolve() + .Methods.FirstOrDefault(md => md.Name == "TryParse" && md.Parameters.Count == 4); + var tryParse = context.Body.Method.Module.Import(tryParseInfo); + 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 decimalctorinfo = + context.Body.Method.Module.Import(typeof(decimal)) + .Resolve() + .Methods.FirstOrDefault( + md => md.IsConstructor && md.Parameters.Count == 1 && md.Parameters [0].ParameterType.FullName == "System.Int32"); + var decimalctor = context.Body.Method.Module.Import(decimalctorinfo); + yield return Instruction.Create(OpCodes.Newobj, decimalctor); + } + } else yield return Instruction.Create(OpCodes.Ldnull); if (isNullable) diff --git a/Xamarin.Forms.Xaml.UnitTests/SetValue.xaml b/Xamarin.Forms.Xaml.UnitTests/SetValue.xaml index da844ed0..75784273 100644 --- a/Xamarin.Forms.Xaml.UnitTests/SetValue.xaml +++ b/Xamarin.Forms.Xaml.UnitTests/SetValue.xaml @@ -83,5 +83,6 @@ <d:IgnorableElement /> </ContentView.Content> </ContentView> + <local:MockViewWithValues x:Name="mockView0" UShort="32" ADecimal="42" /> </StackLayout> </ContentPage>
\ No newline at end of file diff --git a/Xamarin.Forms.Xaml.UnitTests/SetValue.xaml.cs b/Xamarin.Forms.Xaml.UnitTests/SetValue.xaml.cs index 2226402c..3343ed7a 100644 --- a/Xamarin.Forms.Xaml.UnitTests/SetValue.xaml.cs +++ b/Xamarin.Forms.Xaml.UnitTests/SetValue.xaml.cs @@ -2,6 +2,7 @@ using System.Linq; using NUnit.Framework; +using Xamarin.Forms.Core.UnitTests; namespace Xamarin.Forms.Xaml.UnitTests { @@ -13,6 +14,12 @@ namespace Xamarin.Forms.Xaml.UnitTests } } + public class MockViewWithValues : View + { + public UInt16 UShort { get; set; } + public decimal ADecimal { get; set; } + } + public partial class SetValue : ContentPage { public SetValue () @@ -34,6 +41,18 @@ namespace Xamarin.Forms.Xaml.UnitTests [TestFixture] public class Tests { + [SetUp] + public void Setup() + { + Device.PlatformServices = new MockPlatformServices(); + } + + [TearDown] + public void TearDown() + { + Device.PlatformServices = null; + } + [TestCase (false)] [TestCase (true)] public void SetValueToBP (bool useCompiledXaml) @@ -215,6 +234,15 @@ namespace Xamarin.Forms.Xaml.UnitTests var page = new SetValue(useCompiledXaml); Assert.That(page.contentview2.Content, Is.TypeOf<Label>()); } + + [TestCase(false)] + [TestCase(true)] + public void MorePrimitiveTypes(bool useCompiledXaml) + { + var page = new SetValue(useCompiledXaml); + Assert.AreEqual((ushort)32, page.mockView0.UShort); + Assert.AreEqual((decimal)42, page.mockView0.ADecimal); + } } } }
\ No newline at end of file diff --git a/Xamarin.Forms.Xaml.UnitTests/X2009Primitives.xaml b/Xamarin.Forms.Xaml.UnitTests/X2009Primitives.xaml index a7a91edc..c9efad23 100644 --- a/Xamarin.Forms.Xaml.UnitTests/X2009Primitives.xaml +++ b/Xamarin.Forms.Xaml.UnitTests/X2009Primitives.xaml @@ -37,6 +37,10 @@ <x:TimeSpan x:Key="defaultTimeSpan"/> <x:Uri x:Key="anUri">http://xamarin.com/forms</x:Uri> <x:Uri x:Key="defaultUri"/> + <x:SByte x:Key="aSByte">42</x:SByte> + <x:UInt16 x:Key="aUInt16">43</x:UInt16> + <x:UInt32 x:Key="aUInt32">44</x:UInt32> + <x:UInt64 x:Key="aUInt64">45</x:UInt64> </ResourceDictionary> </ContentPage.Resources> </ContentPage>
\ No newline at end of file diff --git a/Xamarin.Forms.Xaml.UnitTests/X2009Primitives.xaml.cs b/Xamarin.Forms.Xaml.UnitTests/X2009Primitives.xaml.cs index 60ead7ca..d7ef6eac 100644 --- a/Xamarin.Forms.Xaml.UnitTests/X2009Primitives.xaml.cs +++ b/Xamarin.Forms.Xaml.UnitTests/X2009Primitives.xaml.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using NUnit.Framework; using Xamarin.Forms; +using Xamarin.Forms.Core.UnitTests; namespace Xamarin.Forms.Xaml.UnitTests { @@ -22,6 +23,18 @@ namespace Xamarin.Forms.Xaml.UnitTests [TestFixture] public class Tests { + [SetUp] + public void Setup() + { + Device.PlatformServices = new MockPlatformServices(); + } + + [TearDown] + public void TearDown() + { + Device.PlatformServices = null; + } + [TestCase (false)] [TestCase (true)] public void SupportsXString (bool useCompiledXaml) @@ -144,11 +157,17 @@ namespace Xamarin.Forms.Xaml.UnitTests Assert.That (defaultDouble, Is.TypeOf<double> ()); Assert.AreEqual (default(double), (double)defaultDouble, .0001d); - Assert.True (layout.Resources.ContainsKey ("aByte")); + Assert.True(layout.Resources.ContainsKey("aByte")); var aByte = layout.Resources ["aByte"]; - Assert.NotNull (aByte); - Assert.That (aByte, Is.TypeOf<byte> ()); - Assert.AreEqual (54, (byte)aByte); + Assert.NotNull(aByte); + Assert.That(aByte, Is.TypeOf<byte>()); + Assert.AreEqual(54, (byte)aByte); + + Assert.True(layout.Resources.ContainsKey("aSByte")); + var aSByte = layout.Resources ["aSByte"]; + Assert.NotNull(aSByte); + Assert.That(aSByte, Is.TypeOf<sbyte>()); + Assert.AreEqual(42, (sbyte)aSByte); Assert.True (layout.Resources.ContainsKey ("defaultByte")); var defaultByte = layout.Resources ["defaultByte"]; @@ -156,11 +175,17 @@ namespace Xamarin.Forms.Xaml.UnitTests Assert.That (defaultByte, Is.TypeOf<byte> ()); Assert.AreEqual (default(byte), (byte)defaultByte); - Assert.True (layout.Resources.ContainsKey ("anInt16")); + Assert.True(layout.Resources.ContainsKey("anInt16")); var anInt16 = layout.Resources ["anInt16"]; - Assert.NotNull (anInt16); - Assert.That (anInt16, Is.TypeOf<short> ()); - Assert.AreEqual (43, (short)anInt16); + Assert.NotNull(anInt16); + Assert.That(anInt16, Is.TypeOf<short>()); + Assert.AreEqual(43, (short)anInt16); + + Assert.True(layout.Resources.ContainsKey("aUInt16")); + var aUInt16 = layout.Resources ["aUInt16"]; + Assert.NotNull(aUInt16); + Assert.That(aUInt16, Is.TypeOf<ushort>()); + Assert.AreEqual(43, (ushort)aUInt16); Assert.True (layout.Resources.ContainsKey ("defaultInt16")); var defaultInt16 = layout.Resources ["defaultInt16"]; @@ -168,11 +193,17 @@ namespace Xamarin.Forms.Xaml.UnitTests Assert.That (defaultInt16, Is.TypeOf<short> ()); Assert.AreEqual (default(short), (short)defaultInt16); - Assert.True (layout.Resources.ContainsKey ("anInt32")); + Assert.True(layout.Resources.ContainsKey("anInt32")); var anInt32 = layout.Resources ["anInt32"]; - Assert.NotNull (anInt32); - Assert.That (anInt32, Is.TypeOf<int> ()); - Assert.AreEqual (44, (int)anInt32); + Assert.NotNull(anInt32); + Assert.That(anInt32, Is.TypeOf<int>()); + Assert.AreEqual(44, (int)anInt32); + + Assert.True(layout.Resources.ContainsKey("aUInt32")); + var aUInt32 = layout.Resources ["aUInt32"]; + Assert.NotNull(aUInt32); + Assert.That(aUInt32, Is.TypeOf<uint>()); + Assert.AreEqual(44, (uint)aUInt32); Assert.True (layout.Resources.ContainsKey ("defaultInt32")); var defaultInt32 = layout.Resources ["defaultInt32"]; @@ -180,11 +211,17 @@ namespace Xamarin.Forms.Xaml.UnitTests Assert.That (defaultInt32, Is.TypeOf<int> ()); Assert.AreEqual (default(int), (int)defaultInt32); - Assert.True (layout.Resources.ContainsKey ("anInt64")); + Assert.True(layout.Resources.ContainsKey("anInt64")); var anInt64 = layout.Resources ["anInt64"]; - Assert.NotNull (anInt64); - Assert.That (anInt64, Is.TypeOf<long> ()); - Assert.AreEqual (45, (long)anInt64); + Assert.NotNull(anInt64); + Assert.That(anInt64, Is.TypeOf<long>()); + Assert.AreEqual(45, (long)anInt64); + + Assert.True(layout.Resources.ContainsKey("aUInt64")); + var aUInt64 = layout.Resources ["aUInt64"]; + Assert.NotNull(aUInt64); + Assert.That(aUInt64, Is.TypeOf<ulong>()); + Assert.AreEqual(45, (ulong)aUInt64); Assert.True (layout.Resources.ContainsKey ("defaultInt64")); var defaultInt64 = layout.Resources ["defaultInt64"]; diff --git a/Xamarin.Forms.Xaml/CreateValuesVisitor.cs b/Xamarin.Forms.Xaml/CreateValuesVisitor.cs index 25a936fa..c2331bf1 100644 --- a/Xamarin.Forms.Xaml/CreateValuesVisitor.cs +++ b/Xamarin.Forms.Xaml/CreateValuesVisitor.cs @@ -296,73 +296,87 @@ namespace Xamarin.Forms.Xaml { var valuestring = ((ValueNode)node.CollectionItems[0]).Value as string; - if (nodeType == typeof (bool)) - { - bool outbool; - if (bool.TryParse(valuestring, out outbool)) - value = outbool; + if (nodeType == typeof(SByte)) { + sbyte retval; + if (sbyte.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval)) + return retval; } - else if (nodeType == typeof (char)) - { - char retval; - if (char.TryParse(valuestring, out retval)) - value = retval; + if (nodeType == typeof(Int16)) { + short retval; + if (short.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval)) + return retval; } - else if (nodeType == typeof (string)) - value = valuestring; - else if (nodeType == typeof (decimal)) - { - decimal retval; - if (decimal.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval)) - value = retval; + if (nodeType == typeof(Int32)) { + int retval; + if (int.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval)) + return retval; } - else if (nodeType == typeof (float)) - { + if (nodeType == typeof(Int64)) { + long retval; + if (long.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval)) + return retval; + } + if (nodeType == typeof(Byte)) { + byte retval; + if (byte.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval)) + return retval; + } + if (nodeType == typeof(UInt16)) { + ushort retval; + if (ushort.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval)) + return retval; + } + if (nodeType == typeof(UInt32)) { + uint retval; + if (uint.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval)) + return retval; + } + if (nodeType == typeof(UInt64)) { + ulong retval; + if (ulong.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval)) + return retval; + } + if (nodeType == typeof(Single)) { float retval; if (float.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval)) - value = retval; + return retval; } - else if (nodeType == typeof (double)) - { + if (nodeType == typeof(Double)) { double retval; if (double.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval)) - value = retval; - } - else if (nodeType == typeof (byte)) - { - byte retval; - if (byte.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval)) - value = retval; + return retval; } - else if (nodeType == typeof (short)) + if (nodeType == typeof (Boolean)) { - short retval; - if (short.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval)) - value = retval; + bool outbool; + if (bool.TryParse(valuestring, out outbool)) + return outbool; } - else if (nodeType == typeof (int)) - { - int retval; - if (int.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval)) - value = retval; + if (nodeType == typeof(TimeSpan)) { + TimeSpan retval; + if (TimeSpan.TryParse(valuestring, CultureInfo.InvariantCulture, out retval)) + return retval; } - else if (nodeType == typeof (long)) + if (nodeType == typeof (char)) { - long retval; - if (long.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval)) - value = retval; + char retval; + if (char.TryParse(valuestring, out retval)) + return retval; } - else if (nodeType == typeof (TimeSpan)) + if (nodeType == typeof (string)) + return valuestring; + if (nodeType == typeof (decimal)) { - TimeSpan retval; - if (TimeSpan.TryParse(valuestring, CultureInfo.InvariantCulture, out retval)) - value = retval; + decimal retval; + if (decimal.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval)) + return retval; } + else if (nodeType == typeof (Uri)) { Uri retval; if (Uri.TryCreate(valuestring, UriKind.RelativeOrAbsolute, out retval)) - value = retval; + return retval; } } return value; diff --git a/Xamarin.Forms.Xaml/TypeConversionExtensions.cs b/Xamarin.Forms.Xaml/TypeConversionExtensions.cs index 7e377ea4..a29b3af2 100644 --- a/Xamarin.Forms.Xaml/TypeConversionExtensions.cs +++ b/Xamarin.Forms.Xaml/TypeConversionExtensions.cs @@ -130,23 +130,43 @@ namespace Xamarin.Forms.Xaml //Obvious Built-in conversions if (toType.GetTypeInfo().IsEnum) return Enum.Parse(toType, str); - //TODO supports Int16, 64, Byte, Char, ... - if (toType == typeof (Int32)) + if (toType == typeof(SByte)) + return SByte.Parse(str, CultureInfo.InvariantCulture); + if (toType == typeof(Int16)) + return Int16.Parse(str, CultureInfo.InvariantCulture); + if (toType == typeof(Int32)) return Int32.Parse(str, CultureInfo.InvariantCulture); - if (toType == typeof (float)) + if (toType == typeof(Int64)) + return Int64.Parse(str, CultureInfo.InvariantCulture); + if (toType == typeof(Byte)) + return Byte.Parse(str, CultureInfo.InvariantCulture); + if (toType == typeof(UInt16)) + return UInt16.Parse(str, CultureInfo.InvariantCulture); + if (toType == typeof(UInt32)) + return UInt32.Parse(str, CultureInfo.InvariantCulture); + if (toType == typeof(UInt64)) + return UInt64.Parse(str, CultureInfo.InvariantCulture); + if (toType == typeof (Single)) return Single.Parse(str, CultureInfo.InvariantCulture); - if (toType == typeof (double)) + if (toType == typeof (Double)) return Double.Parse(str, CultureInfo.InvariantCulture); - if (toType == typeof (bool)) + if (toType == typeof (Boolean)) return Boolean.Parse(str); if (toType == typeof (TimeSpan)) return TimeSpan.Parse(str, CultureInfo.InvariantCulture); if (toType == typeof (DateTime)) return DateTime.Parse(str, CultureInfo.InvariantCulture); - if (toType == typeof (string) && str.StartsWith("{}", StringComparison.Ordinal)) + if (toType == typeof(Char)) { + char c = '\0'; + Char.TryParse(str, out c); + return c; + } + if (toType == typeof (String) && str.StartsWith("{}", StringComparison.Ordinal)) return str.Substring(2); - if (toType == typeof (string)) + if (toType == typeof (String)) return value; + if (toType == typeof(Decimal)) + return Decimal.Parse(str, CultureInfo.InvariantCulture); } //if there's an implicit conversion, convert |