зеркало из https://github.com/DeGsoft/maui-linux.git
[XamlC][Bindings] value types in compiled bindings (#1604)
support value types in path of bindings that will be compiled. fixes #1558
This commit is contained in:
Родитель
a1af9344b7
Коммит
f47c5e3a02
|
@ -477,26 +477,33 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
getter.Body.InitLocals = true;
|
||||
var il = getter.Body.GetILProcessor();
|
||||
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
if (tSourceRef.IsValueType)
|
||||
il.Emit(Ldarga_S, (byte)0);
|
||||
else
|
||||
il.Emit(Ldarg_0);
|
||||
|
||||
if (properties != null && properties.Count != 0) {
|
||||
foreach (var propTuple in properties) {
|
||||
var property = propTuple.Item1;
|
||||
var indexerArg = propTuple.Item2;
|
||||
if (indexerArg != null) {
|
||||
if (property.GetMethod.Parameters [0].ParameterType == module.TypeSystem.String)
|
||||
il.Emit(OpCodes.Ldstr, indexerArg);
|
||||
il.Emit(Ldstr, indexerArg);
|
||||
else if (property.GetMethod.Parameters [0].ParameterType == module.TypeSystem.Int32) {
|
||||
int index;
|
||||
if (!int.TryParse(indexerArg, out index))
|
||||
throw new XamlParseException($"Binding: {indexerArg} could not be parsed as an index for a {property.Name}", node as IXmlLineInfo);
|
||||
il.Emit(OpCodes.Ldc_I4, index);
|
||||
il.Emit(Ldc_I4, index);
|
||||
}
|
||||
}
|
||||
il.Emit(OpCodes.Callvirt, module.ImportReference(property.GetMethod));
|
||||
if (property.GetMethod.IsVirtual)
|
||||
il.Emit(Callvirt, module.ImportReference(property.GetMethod));
|
||||
else
|
||||
il.Emit(Call, module.ImportReference(property.GetMethod));
|
||||
}
|
||||
}
|
||||
|
||||
il.Emit(OpCodes.Ret);
|
||||
il.Emit(Ret);
|
||||
|
||||
context.Body.Method.DeclaringType.Methods.Add(getter);
|
||||
|
||||
|
@ -509,9 +516,9 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
// IL_0008: ldftn string class Test::'<Main>m__0'(class ViewModel)
|
||||
// IL_000e: newobj instance void class [mscorlib]System.Func`2<class ViewModel, string>::'.ctor'(object, native int)
|
||||
|
||||
yield return Instruction.Create(OpCodes.Ldnull);
|
||||
yield return Instruction.Create(OpCodes.Ldftn, getter);
|
||||
yield return Instruction.Create(OpCodes.Newobj, module.ImportReference(funcCtor));
|
||||
yield return Create(Ldnull);
|
||||
yield return Create(Ldftn, getter);
|
||||
yield return Create(Newobj, module.ImportReference(funcCtor));
|
||||
}
|
||||
|
||||
static IEnumerable<Instruction> CompiledBindingGetSetter(TypeReference tSourceRef, TypeReference tPropertyRef, IList<Tuple<PropertyDefinition, string>> properties, ElementNode node, ILContext context)
|
||||
|
@ -555,7 +562,10 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
yield break;
|
||||
}
|
||||
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
if (tSourceRef.IsValueType)
|
||||
il.Emit(Ldarga_S, (byte)0);
|
||||
else
|
||||
il.Emit(Ldarg_0);
|
||||
for (int i = 0; i < properties.Count - 1; i++) {
|
||||
var property = properties[i].Item1;
|
||||
var indexerArg = properties[i].Item2;
|
||||
|
@ -569,7 +579,10 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
il.Emit(OpCodes.Ldc_I4, index);
|
||||
}
|
||||
}
|
||||
il.Emit(OpCodes.Callvirt, module.ImportReference(property.GetMethod));
|
||||
if (property.GetMethod.IsVirtual)
|
||||
il.Emit(Callvirt, module.ImportReference(property.GetMethod));
|
||||
else
|
||||
il.Emit(Call, module.ImportReference(property.GetMethod));
|
||||
}
|
||||
|
||||
var indexer = properties.Last().Item2;
|
||||
|
@ -583,8 +596,16 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
il.Emit(OpCodes.Ldc_I4, index);
|
||||
}
|
||||
}
|
||||
il.Emit(OpCodes.Ldarg_1);
|
||||
il.Emit(OpCodes.Callvirt, module.ImportReference(setterRef));
|
||||
if (tPropertyRef.IsValueType)
|
||||
il.Emit(Ldarga_S, (byte)1);
|
||||
else
|
||||
il.Emit(Ldarg_1);
|
||||
|
||||
if (setterRef.IsVirtual)
|
||||
il.Emit(Callvirt, module.ImportReference(setterRef));
|
||||
else
|
||||
il.Emit(Call, module.ImportReference(setterRef));
|
||||
|
||||
il.Emit(OpCodes.Ret);
|
||||
|
||||
context.Body.Method.DeclaringType.Methods.Add(setter);
|
||||
|
@ -628,7 +649,7 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
|
||||
for (int i = 0; i < properties.Count; i++) {
|
||||
var tuple = properties [i];
|
||||
var partGetter = new MethodDefinition($"<{context.Body.Method.Name}>typedBindingsM__{typedBindingCount++}", MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static, tPropertyRef) {
|
||||
var partGetter = new MethodDefinition($"<{context.Body.Method.Name}>typedBindingsM__{typedBindingCount++}", MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static, module.TypeSystem.Object) {
|
||||
Parameters = {
|
||||
new ParameterDefinition(tSourceRef)
|
||||
},
|
||||
|
@ -638,7 +659,23 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
};
|
||||
partGetter.Body.InitLocals = true;
|
||||
var il = partGetter.Body.GetILProcessor();
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
|
||||
if (i == 0) { //return self
|
||||
il.Emit(Ldarg_0);
|
||||
if (tSourceRef.IsValueType)
|
||||
il.Emit(Box, module.ImportReference(tSourceRef));
|
||||
|
||||
il.Emit(Ret);
|
||||
context.Body.Method.DeclaringType.Methods.Add(partGetter);
|
||||
partGetters.Add(partGetter);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tSourceRef.IsValueType)
|
||||
il.Emit(Ldarga_S, (byte)0);
|
||||
else
|
||||
il.Emit(Ldarg_0);
|
||||
var lastGetterTypeRef = tSourceRef;
|
||||
for (int j = 0; j < i; j++) {
|
||||
var propTuple = properties [j];
|
||||
var property = propTuple.Item1;
|
||||
|
@ -653,8 +690,15 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
il.Emit(OpCodes.Ldc_I4, index);
|
||||
}
|
||||
}
|
||||
il.Emit(OpCodes.Callvirt, module.ImportReference(property.GetMethod));
|
||||
if (property.GetMethod.IsVirtual)
|
||||
il.Emit(Callvirt, module.ImportReference(property.GetMethod));
|
||||
else
|
||||
il.Emit(Call, module.ImportReference(property.GetMethod));
|
||||
lastGetterTypeRef = property.PropertyType;
|
||||
}
|
||||
if (lastGetterTypeRef.IsValueType)
|
||||
il.Emit(Box, module.ImportReference(lastGetterTypeRef));
|
||||
|
||||
il.Emit(OpCodes.Ret);
|
||||
context.Body.Method.DeclaringType.Methods.Add(partGetter);
|
||||
partGetters.Add(partGetter);
|
||||
|
|
|
@ -12,10 +12,11 @@
|
|||
<Label Text="{Binding Path=Text}" x:Name="label1" />
|
||||
<Label Text="{Binding Model.Text}" x:Name="label2" />
|
||||
<Label Text="{Binding Model[3]}" x:Name="label3" />
|
||||
<Label Text="{Binding}" x:Name="label4" x:DataType="sys:String"/>
|
||||
<Label Text="{Binding}" x:Name="label4" x:DataType="sys:String"/>
|
||||
<Label Text="{Binding I}" x:Name="label5" />
|
||||
<Label Text="{Binding Model.Text}" x:Name="label6" x:DataType="local:MockStructViewModel" />
|
||||
<Entry Text="{Binding Text, Mode=TwoWay}" x:Name="entry0"/>
|
||||
</StackLayout>
|
||||
|
||||
<Label Text="{Binding Text}" x:Name="labelWithUncompiledBinding" />
|
||||
</StackLayout>
|
||||
</ContentPage>
|
|
@ -41,8 +41,11 @@ namespace Xamarin.Forms.Xaml.UnitTests
|
|||
[TestCase(true)]
|
||||
public void Test(bool useCompiledXaml)
|
||||
{
|
||||
if (useCompiledXaml)
|
||||
MockCompiler.Compile(typeof(BindingsCompiler));
|
||||
var vm = new MockViewModel {
|
||||
Text = "Text0",
|
||||
I = 42,
|
||||
Model = new MockViewModel {
|
||||
Text = "Text1"
|
||||
},
|
||||
|
@ -51,12 +54,22 @@ namespace Xamarin.Forms.Xaml.UnitTests
|
|||
|
||||
var layout = new BindingsCompiler(useCompiledXaml);
|
||||
layout.BindingContext = vm;
|
||||
layout.label6.BindingContext = new MockStructViewModel {
|
||||
Model = new MockViewModel {
|
||||
Text = "text6"
|
||||
}
|
||||
};
|
||||
|
||||
//testing paths
|
||||
Assert.AreEqual("Text0", layout.label0.Text);
|
||||
Assert.AreEqual("Text0", layout.label1.Text);
|
||||
Assert.AreEqual("Text1", layout.label2.Text);
|
||||
Assert.AreEqual("TextIndex", layout.label3.Text);
|
||||
|
||||
//value types
|
||||
Assert.That(layout.label5.Text, Is.EqualTo("42"));
|
||||
Assert.That(layout.label6.Text, Is.EqualTo("text6"));
|
||||
|
||||
//testing selfPath
|
||||
layout.label4.BindingContext = "Self";
|
||||
Assert.AreEqual("Self", layout.label4.Text);
|
||||
|
@ -77,13 +90,21 @@ namespace Xamarin.Forms.Xaml.UnitTests
|
|||
}
|
||||
}
|
||||
|
||||
struct MockStructViewModel
|
||||
{
|
||||
public string Text { get; set; }
|
||||
public int I { get; set; }
|
||||
public MockViewModel Model { get; set; }
|
||||
}
|
||||
|
||||
class MockViewModel : INotifyPropertyChanged
|
||||
{
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
public MockViewModel(string text = null)
|
||||
public MockViewModel(string text = null, int i = -1)
|
||||
{
|
||||
_text = text;
|
||||
_i = i;
|
||||
}
|
||||
|
||||
string _text;
|
||||
|
@ -98,6 +119,17 @@ namespace Xamarin.Forms.Xaml.UnitTests
|
|||
}
|
||||
}
|
||||
|
||||
int _i;
|
||||
public int I {
|
||||
get { return _i; }
|
||||
set {
|
||||
if (_i == value)
|
||||
return;
|
||||
_i = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
MockViewModel _model;
|
||||
public MockViewModel Model {
|
||||
get { return _model; }
|
||||
|
|
Загрузка…
Ссылка в новой задаче