2 * Copyright(c) 2022 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 using System.Collections.Generic;
24 using Mono.Cecil.Rocks;
25 using Tizen.NUI.Binding;
26 using Tizen.NUI.Binding.Internals;
30 using static Mono.Cecil.Cil.Instruction;
31 using static Mono.Cecil.Cil.OpCodes;
33 namespace Tizen.NUI.Xaml.Build.Tasks
35 class SetPropertiesVisitor : IXamlNodeVisitor
38 static int typedBindingCount;
40 static readonly IList<XmlName> skips = new List<XmlName>
43 XmlName.xTypeArguments,
45 XmlName.xFactoryMethod,
50 public SetPropertiesVisitor(ILContext context, bool stopOnResourceDictionary = false)
53 Module = context.Body.Method.Module;
54 StopOnResourceDictionary = stopOnResourceDictionary;
57 public ILContext Context { get; }
58 public bool StopOnResourceDictionary { get; }
59 public TreeVisitingMode VisitingMode => TreeVisitingMode.BottomUp;
60 public bool StopOnDataTemplate => true;
61 public bool VisitNodeOnDataTemplate => true;
62 public bool SkipChildren(INode node, INode parentNode) => false;
64 public bool IsResourceDictionary(ElementNode node)
66 var parentVar = Context.Variables[(IElementNode)node];
67 return parentVar.VariableType.FullName == "Tizen.NUI.Binding.ResourceDictionary"
68 || parentVar.VariableType.Resolve().BaseType?.FullName == "Tizen.NUI.Binding.ResourceDictionary";
71 ModuleDefinition Module { get; }
73 public void Visit(ValueNode node, INode parentNode)
75 //TODO support Label text as element
77 if (!TryGetPropertyName(node, parentNode, out propertyName))
79 if (!IsCollectionItem(node, parentNode))
81 string contentProperty;
82 if (!Context.Variables.ContainsKey((IElementNode)parentNode))
84 var parentVar = Context.Variables[(IElementNode)parentNode];
85 if ((contentProperty = GetContentProperty(parentVar.VariableType)) != null)
86 propertyName = new XmlName(((IElementNode)parentNode).NamespaceURI, contentProperty);
91 if (TrySetRuntimeName(propertyName, Context.Variables[(IElementNode)parentNode], node))
93 if (skips.Contains(propertyName))
95 if (parentNode is IElementNode && ((IElementNode)parentNode).SkipProperties.Contains (propertyName))
97 if (propertyName.Equals(XamlParser.McUri, "Ignorable"))
99 Context.IL.Append(SetPropertyValue(Context.Variables [(IElementNode)parentNode], propertyName, node, Context, node));
102 public void Visit(MarkupNode node, INode parentNode)
106 public void Visit(ElementNode node, INode parentNode)
108 XmlName propertyName = XmlName.Empty;
110 //Simplify ListNodes with single elements
111 var pList = parentNode as ListNode;
112 if (pList != null && pList.CollectionItems.Count == 1) {
113 propertyName = pList.XmlName;
114 parentNode = parentNode.Parent;
117 if ((propertyName != XmlName.Empty || TryGetPropertyName(node, parentNode, out propertyName)) && skips.Contains(propertyName))
120 if (propertyName == XmlName._CreateContent) {
121 SetDataTemplate((IElementNode)parentNode, node, Context, node);
125 //if this node is an IMarkupExtension, invoke ProvideValue() and replace the variable
126 var vardef = Context.Variables[node];
127 var vardefref = new VariableDefinitionReference(vardef);
128 var localName = propertyName.LocalName;
129 TypeReference declaringTypeReference = null;
130 FieldReference bpRef = null;
132 PropertyDefinition propertyRef = null;
133 if (parentNode is IElementNode && propertyName != XmlName.Empty) {
134 bpRef = GetBindablePropertyReference(Context.Variables [(IElementNode)parentNode], propertyName.NamespaceURI, ref localName, out _, Context, node);
135 propertyRef = Context.Variables [(IElementNode)parentNode].VariableType.GetProperty(pd => pd.Name == localName, out declaringTypeReference);
137 Context.IL.Append(ProvideValue(vardefref, Context, Module, node, bpRef:bpRef, propertyRef:propertyRef, propertyDeclaringTypeRef: declaringTypeReference));
138 if (vardef != vardefref.VariableDefinition)
140 vardef = vardefref.VariableDefinition;
141 Context.Body.Variables.Add(vardef);
142 Context.Variables[node] = vardef;
145 if (propertyName != XmlName.Empty) {
146 if (skips.Contains(propertyName))
148 if (parentNode is IElementNode && ((IElementNode)parentNode).SkipProperties.Contains (propertyName))
151 Context.IL.Append(SetPropertyValue(Context.Variables[(IElementNode)parentNode], propertyName, node, Context, node));
153 else if (IsCollectionItem(node, parentNode) && parentNode is IElementNode) {
154 var parentVar = Context.Variables[(IElementNode)parentNode];
155 string contentProperty;
157 bool isAdded = false;
159 if (parentVar.VariableType.IsArray)
163 else if (CanAddToResourceDictionary(parentVar, parentVar.VariableType, node, node, Context))
165 Context.IL.Emit(Ldloc, parentVar);
166 Context.IL.Append(AddToResourceDictionary(node, node, Context));
169 // Collection element, implicit content, or implicit collection element.
170 else if (parentVar.VariableType.GetMethods(md => md.Name == "Add" && md.Parameters.Count == 1, Module).Any())
172 var elementType = parentVar.VariableType;
173 var paramType = Context.Variables[node].VariableType;
175 foreach (var adderTuple in elementType.GetMethods(md => md.Name == "Add" && md.Parameters.Count == 1, Module))
177 var adderRef = Module.ImportReference(adderTuple.Item1);
178 adderRef = Module.ImportReference(adderRef.ResolveGenericParameters(adderTuple.Item2, Module));
180 if (IsAddMethodOfCollection(Module, adderRef.Resolve()))
184 else if (paramType.InheritsFromOrImplements(adderTuple.Item1.Parameters[0].ParameterType.FullName))
191 Context.IL.Emit(Ldloc, parentVar);
192 Context.IL.Emit(Ldloc, vardef);
193 Context.IL.Emit(Callvirt, adderRef);
194 if (adderRef.ReturnType.FullName != "System.Void")
195 Context.IL.Emit(Pop);
201 if (!isAdded && (contentProperty = GetContentProperty(parentVar.VariableType)) != null)
203 var name = new XmlName(node.NamespaceURI, contentProperty);
204 if (skips.Contains(name))
206 if (parentNode is IElementNode && ((IElementNode)parentNode).SkipProperties.Contains(propertyName))
208 Context.IL.Append(SetPropertyValue(Context.Variables[(IElementNode)parentNode], name, node, Context, node));
214 throw new XamlParseException($"Can not set the content of {((IElementNode)parentNode).XmlType.Name} as it doesn't have a ContentPropertyAttribute", node);
217 else if (IsCollectionItem(node, parentNode) && parentNode is ListNode)
220 // IL_000e: callvirt instance class [mscorlib]System.Collections.Generic.IList`1<!0> class [Tizen.NUI.Xaml.Core]Tizen.NUI.Xaml.Layout`1<class [Tizen.NUI.Xaml.Core]Tizen.NUI.Xaml.View>::get_Children()
222 // IL_0014: callvirt instance void class [mscorlib]System.Collections.Generic.ICollection`1<class [Tizen.NUI.Xaml.Core]Tizen.NUI.Xaml.View>::Add(!0)
224 var parentList = (ListNode)parentNode;
225 var parent = Context.Variables[((IElementNode)parentNode.Parent)];
227 if (skips.Contains(parentList.XmlName))
229 if (parentNode is IElementNode && ((IElementNode)parentNode).SkipProperties.Contains (propertyName))
231 var elementType = parent.VariableType;
232 var localname = parentList.XmlName.LocalName;
234 TypeReference propertyType;
235 Context.IL.Append(GetPropertyValue(parent, parentList.XmlName, Context, node, out propertyType));
237 if (CanAddToResourceDictionary(parent, propertyType, node, node, Context)) {
238 Context.IL.Append(AddToResourceDictionary(node, node, Context));
241 var adderTuple = propertyType.GetMethods(md => md.Name == "Add" && md.Parameters.Count == 1, Module).FirstOrDefault();
242 if (adderTuple == null)
243 throw new XamlParseException($"Can not Add() elements to {parent.VariableType}.{localname}", node);
244 var adderRef = Module.ImportReference(adderTuple.Item1);
245 adderRef = Module.ImportReference(adderRef.ResolveGenericParameters(adderTuple.Item2, Module));
247 Context.IL.Emit(OpCodes.Ldloc, vardef);
248 Context.IL.Emit(OpCodes.Callvirt, adderRef);
249 if (adderRef.ReturnType.FullName != "System.Void")
250 Context.IL.Emit(OpCodes.Pop);
254 private static bool IsAddMethodOfCollection(ModuleDefinition module, MethodDefinition methodDef)
256 return module.ImportReference(typeof(List<string>)).InheritsFromOrImplements(methodDef.DeclaringType);
259 public void Visit(RootNode node, INode parentNode)
263 public void Visit(ListNode node, INode parentNode)
267 public static bool TryGetPropertyName(INode node, INode parentNode, out XmlName name)
269 name = default(XmlName);
270 var parentElement = parentNode as IElementNode;
271 if (parentElement == null)
273 foreach (var kvp in parentElement.Properties)
275 if (kvp.Value != node)
283 static bool IsCollectionItem(INode node, INode parentNode)
285 var parentList = parentNode as IListNode;
286 if (parentList == null)
288 return parentList.CollectionItems.Contains(node);
291 internal static string GetContentProperty(TypeReference typeRef)
293 var typeDef = typeRef.ResolveCached();
294 var attributes = typeDef.CustomAttributes;
296 attributes.FirstOrDefault(cad => ContentPropertyAttribute.ContentPropertyTypes.Contains(cad.AttributeType.FullName));
298 return attr.ConstructorArguments[0].Value as string;
299 if (typeDef.BaseType == null)
301 return GetContentProperty(typeDef.BaseType);
304 public static IEnumerable<Instruction> ProvideValue(VariableDefinitionReference vardefref, ILContext context,
305 ModuleDefinition module, ElementNode node, FieldReference bpRef = null,
306 PropertyReference propertyRef = null, TypeReference propertyDeclaringTypeRef = null)
308 GenericInstanceType markupExtension;
309 IList<TypeReference> genericArguments;
310 if (vardefref.VariableDefinition.VariableType.FullName == "Tizen.NUI.Xaml.ArrayExtension" &&
311 vardefref.VariableDefinition.VariableType.ImplementsGenericInterface("Tizen.NUI.Xaml.IMarkupExtension`1",
312 out markupExtension, out genericArguments))
314 var markExt = markupExtension.ResolveCached();
315 var provideValueInfo = markExt.Methods.First(md => md.Name == "ProvideValue");
316 var provideValue = module.ImportReference(provideValueInfo);
318 module.ImportReference(provideValue.ResolveGenericParameters(markupExtension, module));
320 var typeNode = node.Properties[new XmlName("", "Type")];
321 TypeReference arrayTypeRef;
322 if (context.TypeExtensions.TryGetValue(typeNode, out arrayTypeRef))
323 vardefref.VariableDefinition = new VariableDefinition(module.ImportReference(arrayTypeRef.MakeArrayType()));
325 vardefref.VariableDefinition = new VariableDefinition(module.ImportReference(genericArguments.First()));
326 yield return Instruction.Create(OpCodes.Ldloc, context.Variables[node]);
327 foreach (var instruction in node.PushServiceProvider(context, bpRef, propertyRef, propertyDeclaringTypeRef))
328 yield return instruction;
329 yield return Instruction.Create(OpCodes.Callvirt, provideValue);
331 if (arrayTypeRef != null)
332 yield return Instruction.Create(OpCodes.Castclass, module.ImportReference(arrayTypeRef.MakeArrayType()));
333 yield return Instruction.Create(OpCodes.Stloc, vardefref.VariableDefinition);
335 else if (vardefref.VariableDefinition.VariableType.ImplementsGenericInterface("Tizen.NUI.Xaml.IMarkupExtension`1",
336 out markupExtension, out genericArguments))
338 var acceptEmptyServiceProvider = vardefref.VariableDefinition.VariableType.GetCustomAttribute(module, (XamlCTask.xamlAssemblyName, XamlCTask.xamlNameSpace, "AcceptEmptyServiceProviderAttribute")) != null;
339 if (vardefref.VariableDefinition.VariableType.FullName == "Tizen.NUI.Xaml.BindingExtension")
340 foreach (var instruction in CompileBindingPath(node, context, vardefref.VariableDefinition))
341 yield return instruction;
343 var markExt = markupExtension.ResolveCached();
344 var provideValueInfo = markExt.Methods.First(md => md.Name == "ProvideValue");
345 var provideValue = module.ImportReference(provideValueInfo);
347 module.ImportReference(provideValue.ResolveGenericParameters(markupExtension, module));
349 vardefref.VariableDefinition = new VariableDefinition(module.ImportReference(genericArguments.First()));
350 yield return Instruction.Create(OpCodes.Ldloc, context.Variables[node]);
351 if (acceptEmptyServiceProvider)
352 yield return Instruction.Create(OpCodes.Ldnull);
354 foreach (var instruction in node.PushServiceProvider(context, bpRef, propertyRef, propertyDeclaringTypeRef))
355 yield return instruction;
356 yield return Instruction.Create(OpCodes.Callvirt, provideValue);
357 yield return Instruction.Create(OpCodes.Stloc, vardefref.VariableDefinition);
359 else if (context.Variables[node].VariableType.ImplementsInterface(module.ImportReference((XamlCTask.xamlAssemblyName, XamlCTask.xamlNameSpace, "IMarkupExtension"))))
361 var acceptEmptyServiceProvider = context.Variables[node].VariableType.GetCustomAttribute(module, (XamlCTask.xamlAssemblyName, XamlCTask.xamlNameSpace, "AcceptEmptyServiceProviderAttribute")) != null;
362 vardefref.VariableDefinition = new VariableDefinition(module.TypeSystem.Object);
363 yield return Create(Ldloc, context.Variables[node]);
364 if (acceptEmptyServiceProvider)
365 yield return Create(Ldnull);
367 foreach (var instruction in node.PushServiceProvider(context, bpRef, propertyRef, propertyDeclaringTypeRef))
368 yield return instruction;
369 yield return Create(Callvirt, module.ImportMethodReference((XamlCTask.xamlAssemblyName, XamlCTask.xamlNameSpace, "IMarkupExtension"),
370 methodName: "ProvideValue",
371 parameterTypes: new[] { ("System.ComponentModel", "System", "IServiceProvider") }));
372 yield return Create(Stloc, vardefref.VariableDefinition);
374 else if (context.Variables[node].VariableType.ImplementsInterface(module.ImportReference((XamlCTask.xamlAssemblyName, XamlCTask.xamlNameSpace, "IValueProvider"))))
376 var acceptEmptyServiceProvider = context.Variables[node].VariableType.GetCustomAttribute(module, (XamlCTask.xamlAssemblyName, XamlCTask.xamlNameSpace, "AcceptEmptyServiceProviderAttribute")) != null;
377 var valueProviderType = context.Variables[node].VariableType;
378 //If the IValueProvider has a ProvideCompiledAttribute that can be resolved, shortcut this
379 var compiledValueProviderName = valueProviderType?.GetCustomAttribute(module, (XamlCTask.xamlAssemblyName, XamlCTask.xamlNameSpace, "ProvideCompiledAttribute"))?.ConstructorArguments?[0].Value as string;
380 Type compiledValueProviderType;
381 if (compiledValueProviderName != null && (compiledValueProviderType = Type.GetType(compiledValueProviderName)) != null) {
382 var compiledValueProvider = Activator.CreateInstance(compiledValueProviderType);
383 var cProvideValue = typeof(ICompiledValueProvider).GetMethods().FirstOrDefault(md => md.Name == "ProvideValue");
384 var instructions = (IEnumerable<Instruction>)cProvideValue.Invoke(compiledValueProvider, new object[] {
386 context.Body.Method.Module,
389 foreach (var i in instructions)
394 vardefref.VariableDefinition = new VariableDefinition(module.TypeSystem.Object);
395 yield return Create(Ldloc, context.Variables[node]);
396 if (acceptEmptyServiceProvider)
397 yield return Create(Ldnull);
399 foreach (var instruction in node.PushServiceProvider(context, bpRef, propertyRef, propertyDeclaringTypeRef))
400 yield return instruction;
401 yield return Create(Callvirt, module.ImportMethodReference((XamlCTask.xamlAssemblyName, XamlCTask.xamlNameSpace, "IValueProvider"),
402 methodName: "ProvideValue",
403 parameterTypes: new[] { ("System.ComponentModel", "System", "IServiceProvider") }));
404 yield return Create(Stloc, vardefref.VariableDefinition);
408 //Once we get compiled IValueProvider, this will move to the BindingExpression
409 static IEnumerable<Instruction> CompileBindingPath(ElementNode node, ILContext context, VariableDefinition bindingExt)
411 //TODO support casting operators
412 var module = context.Module;
415 if (!node.Properties.TryGetValue(new XmlName("", "Path"), out pathNode) && node.CollectionItems.Any())
416 pathNode = node.CollectionItems [0];
417 var path = (pathNode as ValueNode)?.Value as string;
418 BindingMode declaredmode;
419 if ( !node.Properties.TryGetValue(new XmlName("", "Mode"), out INode modeNode)
420 || !Enum.TryParse((modeNode as ValueNode)?.Value as string, true, out declaredmode))
421 declaredmode = BindingMode.TwoWay; //meaning the mode isn't specified in the Binding extension. generate getters, setters, handlers
423 INode dataTypeNode = null;
424 IElementNode n = node;
426 if (n.Properties.TryGetValue(XmlName.xDataType, out dataTypeNode))
428 n = n.Parent as IElementNode;
430 var dataType = (dataTypeNode as ValueNode)?.Value as string;
431 if (dataType == null)
434 var prefix = dataType.Contains(":") ? dataType.Substring(0, dataType.IndexOf(":", StringComparison.Ordinal)) : "";
435 var namespaceuri = node.NamespaceResolver.LookupNamespace(prefix) ?? "";
436 if (!string.IsNullOrEmpty(prefix) && string.IsNullOrEmpty(namespaceuri))
437 throw new XamlParseException($"Undeclared xmlns prefix '{prefix}'", dataTypeNode as IXmlLineInfo);
439 var dtXType = new XmlType(namespaceuri, dataType, null);
441 var tSourceRef = dtXType.GetTypeReference(XmlTypeExtensions.ModeOfGetType.Both, module, (IXmlLineInfo)node);
442 if (tSourceRef == null)
445 var properties = ParsePath(path, tSourceRef, node as IXmlLineInfo, module);
446 var tPropertyRef = properties != null && properties.Any() ? properties.Last().Item1.PropertyType : tSourceRef;
447 tPropertyRef = module.ImportReference(tPropertyRef);
449 var funcRef = module.ImportReference(module.ImportReference(("mscorlib", "System", "Func`2")).MakeGenericInstanceType(new [] { tSourceRef, tPropertyRef }));
450 var actionRef = module.ImportReference(module.ImportReference(("mscorlib", "System", "Action`2")).MakeGenericInstanceType(new [] { tSourceRef, tPropertyRef }));
451 var funcObjRef = module.ImportReference(module.ImportReference(("mscorlib", "System", "Func`2")).MakeGenericInstanceType(new [] { tSourceRef, module.TypeSystem.Object }));
452 var tupleRef = module.ImportReference(module.ImportReference(("mscorlib", "System", "Tuple`2")).MakeGenericInstanceType(new [] { funcObjRef, module.TypeSystem.String}));
453 var typedBindingRef = module.ImportReference(module.ImportReference((XamlCTask.bindingAssemblyName, XamlCTask.bindingInternalNameSpace, "TypedBinding`2")).MakeGenericInstanceType(new [] { tSourceRef, tPropertyRef}));
455 var ctorInfo = module.ImportReference(typedBindingRef.ResolveCached().Methods.FirstOrDefault(md => md.IsConstructor && !md.IsStatic && md.Parameters.Count == 3 ));
456 var ctorinforef = ctorInfo.MakeGeneric(typedBindingRef, funcRef, actionRef, tupleRef);
458 yield return Instruction.Create(OpCodes.Ldloc, bindingExt);
459 foreach (var instruction in CompiledBindingGetGetter(tSourceRef, tPropertyRef, properties, node, context))
460 yield return instruction;
461 if (declaredmode != BindingMode.OneTime && declaredmode != BindingMode.OneWay) { //if the mode is explicitly 1w, or 1t, no need for setters
462 foreach (var instruction in CompiledBindingGetSetter(tSourceRef, tPropertyRef, properties, node, context))
463 yield return instruction;
465 yield return Create(Ldnull);
466 if (declaredmode != BindingMode.OneTime) { //if the mode is explicitly 1t, no need for handlers
467 foreach (var instruction in CompiledBindingGetHandlers(tSourceRef, tPropertyRef, properties, node, context))
468 yield return instruction;
470 yield return Create(Ldnull);
471 yield return Instruction.Create(OpCodes.Newobj, module.ImportReference(ctorinforef));
472 yield return Instruction.Create(OpCodes.Callvirt, module.ImportPropertySetterReference((XamlCTask.bindingAssemblyName, XamlCTask.bindingNameSpace, "BindingExtension"), propertyName: "TypedBinding"));
475 static IList<Tuple<PropertyDefinition, string>> ParsePath(string path, TypeReference tSourceRef, IXmlLineInfo lineInfo, ModuleDefinition module)
477 if (string.IsNullOrWhiteSpace(path))
479 path = path.Trim(' ', '.'); //trim leading or trailing dots
480 var parts = path.Split(new [] { '.' }, StringSplitOptions.RemoveEmptyEntries);
481 var properties = new List<Tuple<PropertyDefinition, string>>();
483 var previousPartTypeRef = tSourceRef;
485 foreach (var part in parts) {
487 string indexArg = null;
488 var lbIndex = p.IndexOf('[');
490 var rbIndex = p.LastIndexOf(']');
492 throw new XamlParseException("Binding: Indexer did not contain closing bracket", lineInfo);
494 var argLength = rbIndex - lbIndex - 1;
496 throw new XamlParseException("Binding: Indexer did not contain arguments", lineInfo);
498 indexArg = p.Substring(lbIndex + 1, argLength).Trim();
499 if (indexArg.Length == 0)
500 throw new XamlParseException("Binding: Indexer did not contain arguments", lineInfo);
502 p = p.Substring(0, lbIndex);
507 var property = previousPartTypeRef.GetProperty(pd => pd.Name == p && pd.GetMethod != null && pd.GetMethod.IsPublic, out _)
508 ?? throw new XamlParseException($"Binding: Property '{p}' not found on '{previousPartTypeRef}'", lineInfo);
509 properties.Add(new Tuple<PropertyDefinition, string>(property,null));
510 previousPartTypeRef = property.PropertyType;
512 if (indexArg != null) {
513 var defaultMemberAttribute = previousPartTypeRef.GetCustomAttribute(module, ("mscorlib", "System.Reflection", "DefaultMemberAttribute"));
514 var indexerName = defaultMemberAttribute?.ConstructorArguments?.FirstOrDefault().Value as string ?? "Item";
515 var indexer = previousPartTypeRef.GetProperty(pd => pd.Name == indexerName && pd.GetMethod != null && pd.GetMethod.IsPublic, out _);
516 properties.Add(new Tuple<PropertyDefinition, string>(indexer, indexArg));
517 if (indexer.PropertyType != module.TypeSystem.String && indexer.PropertyType != module.TypeSystem.Int32)
518 throw new XamlParseException($"Binding: Unsupported indexer index type: {indexer.PropertyType.FullName}", lineInfo);
519 previousPartTypeRef = indexer.PropertyType;
525 static IEnumerable<Instruction> CompiledBindingGetGetter(TypeReference tSourceRef, TypeReference tPropertyRef, IList<Tuple<PropertyDefinition, string>> properties, ElementNode node, ILContext context)
527 // .method private static hidebysig default string '<Main>m__0' (class ViewModel vm) cil managed
529 // .custom instance void class [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::'.ctor'() = (01 00 00 00 ) // ...
532 // IL_0001: callvirt instance class ViewModel class ViewModel::get_Model()
533 // IL_0006: callvirt instance string class ViewModel::get_Text()
537 var module = context.Module;
538 var getter = new MethodDefinition($"<{context.Body.Method.Name}>typedBindingsM__{typedBindingCount++}",
539 MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static,
542 new ParameterDefinition(tSourceRef)
545 new CustomAttribute (module.ImportCtorReference(("mscorlib", "System.Runtime.CompilerServices", "CompilerGeneratedAttribute"), parameterTypes: null))
548 getter.Body.InitLocals = true;
549 var il = getter.Body.GetILProcessor();
551 if (properties == null || properties.Count == 0) { //return self
556 if (tSourceRef.IsValueType)
557 il.Emit(Ldarga_S, (byte)0);
561 foreach (var propTuple in properties) {
562 var property = propTuple.Item1;
563 var indexerArg = propTuple.Item2;
564 if (indexerArg != null) {
565 if (property.GetMethod.Parameters[0].ParameterType == module.TypeSystem.String)
566 il.Emit(Ldstr, indexerArg);
567 else if (property.GetMethod.Parameters[0].ParameterType == module.TypeSystem.Int32) {
569 if (!int.TryParse(indexerArg, out index))
570 throw new XamlParseException($"Binding: {indexerArg} could not be parsed as an index for a {property.Name}", node as IXmlLineInfo);
571 il.Emit(Ldc_I4, index);
574 if (property.GetMethod.IsVirtual)
575 il.Emit(Callvirt, module.ImportReference(property.GetMethod));
577 il.Emit(Call, module.ImportReference(property.GetMethod));
582 context.Body.Method.DeclaringType.Methods.Add(getter);
585 // IL_0008: ldftn string class Test::'<Main>m__0'(class ViewModel)
586 // IL_000e: newobj instance void class [mscorlib]System.Func`2<class ViewModel, string>::'.ctor'(object, native int)
588 yield return Create(Ldnull);
589 yield return Create(Ldftn, getter);
590 yield return Create(Newobj, module.ImportCtorReference(("mscorlib", "System", "Func`2"), paramCount: 2, classArguments: new[] { tSourceRef, tPropertyRef }));
593 static IEnumerable<Instruction> CompiledBindingGetSetter(TypeReference tSourceRef, TypeReference tPropertyRef, IList<Tuple<PropertyDefinition, string>> properties, ElementNode node, ILContext context)
595 if (properties == null || properties.Count == 0) {
596 yield return Create(Ldnull);
600 // .method private static hidebysig default void '<Main>m__1' (class ViewModel vm, string s) cil managed
602 // .custom instance void class [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::'.ctor'() = (01 00 00 00 ) // ....
605 // IL_0001: callvirt instance class ViewModel class ViewModel::get_Model()
607 // IL_0007: callvirt instance void class ViewModel::set_Text(string)
611 var module = context.Module;
612 var setter = new MethodDefinition($"<{context.Body.Method.Name}>typedBindingsM__{typedBindingCount++}",
613 MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static,
614 module.TypeSystem.Void) {
616 new ParameterDefinition(tSourceRef),
617 new ParameterDefinition(tPropertyRef)
620 new CustomAttribute (module.ImportCtorReference(("mscorlib", "System.Runtime.CompilerServices", "CompilerGeneratedAttribute"), parameterTypes: null))
623 setter.Body.InitLocals = true;
625 var il = setter.Body.GetILProcessor();
626 var lastProperty = properties.LastOrDefault();
627 var setterRef = lastProperty?.Item1.SetMethod;
628 if (setterRef == null) {
629 yield return Create(Ldnull); //throw or not ?
633 if (tSourceRef.IsValueType)
634 il.Emit(Ldarga_S, (byte)0);
637 for (int i = 0; i < properties.Count - 1; i++) {
638 var property = properties[i].Item1;
639 var indexerArg = properties[i].Item2;
640 if (indexerArg != null) {
641 if (property.GetMethod.Parameters [0].ParameterType == module.TypeSystem.String)
642 il.Emit(Ldstr, indexerArg);
643 else if (property.GetMethod.Parameters [0].ParameterType == module.TypeSystem.Int32) {
645 if (!int.TryParse(indexerArg, out index))
646 throw new XamlParseException($"Binding: {indexerArg} could not be parsed as an index for a {property.Name}", node as IXmlLineInfo);
647 il.Emit(Ldc_I4, index);
650 if (property.GetMethod.IsVirtual)
651 il.Emit(Callvirt, module.ImportReference(property.GetMethod));
653 il.Emit(Call, module.ImportReference(property.GetMethod));
656 var indexer = properties.Last().Item2;
657 if (indexer != null) {
658 if (lastProperty.Item1.GetMethod.Parameters [0].ParameterType == module.TypeSystem.String)
659 il.Emit(Ldstr, indexer);
660 else if (lastProperty.Item1.GetMethod.Parameters [0].ParameterType == module.TypeSystem.Int32) {
662 if (!int.TryParse(indexer, out index))
663 throw new XamlParseException($"Binding: {indexer} could not be parsed as an index for a {lastProperty.Item1.Name}", node as IXmlLineInfo);
664 il.Emit(Ldc_I4, index);
670 if (setterRef.IsVirtual)
671 il.Emit(Callvirt, module.ImportReference(setterRef));
673 il.Emit(Call, module.ImportReference(setterRef));
677 context.Body.Method.DeclaringType.Methods.Add(setter);
680 // IL_0025: ldftn void class Test::'<Main>m__1'(class ViewModel, string)
681 // IL_002b: newobj instance void class [mscorlib]System.Action`2<class ViewModel, string>::'.ctor'(object, native int)
682 yield return Create(Ldnull);
683 yield return Create(Ldftn, setter);
684 yield return Create(Newobj, module.ImportCtorReference(("mscorlib", "System", "Action`2"),
687 new[] { tSourceRef, tPropertyRef }));
690 static IEnumerable<Instruction> CompiledBindingGetHandlers(TypeReference tSourceRef, TypeReference tPropertyRef, IList<Tuple<PropertyDefinition, string>> properties, ElementNode node, ILContext context)
692 // .method private static hidebysig default object '<Main>m__2'(class ViewModel vm) cil managed {
693 // .custom instance void class [mscorlib] System.Runtime.CompilerServices.CompilerGeneratedAttribute::'.ctor'() = (01 00 00 00 ) // ....
696 // } // end of method Test::<Main>m__2
698 // .method private static hidebysig default object '<Main>m__3' (class ViewModel vm) cil managed {
699 // .custom instance void class [mscorlib] System.Runtime.CompilerServices.CompilerGeneratedAttribute::'.ctor'() = (01 00 00 00 ) // ....
701 // IL_0001: callvirt instance class ViewModel class ViewModel::get_Model()
705 var module = context.Module;
707 var partGetters = new List<MethodDefinition>();
708 if (properties == null || properties.Count == 0) {
709 yield return Instruction.Create(OpCodes.Ldnull);
713 for (int i = 0; i < properties.Count; i++) {
714 var tuple = properties [i];
715 var partGetter = new MethodDefinition($"<{context.Body.Method.Name}>typedBindingsM__{typedBindingCount++}", MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static, module.TypeSystem.Object) {
717 new ParameterDefinition(tSourceRef)
720 new CustomAttribute (module.ImportCtorReference(("mscorlib", "System.Runtime.CompilerServices", "CompilerGeneratedAttribute"), parameterTypes: null))
723 partGetter.Body.InitLocals = true;
724 var il = partGetter.Body.GetILProcessor();
726 if (i == 0) { //return self
728 if (tSourceRef.IsValueType)
729 il.Emit(Box, module.ImportReference(tSourceRef));
732 context.Body.Method.DeclaringType.Methods.Add(partGetter);
733 partGetters.Add(partGetter);
737 if (tSourceRef.IsValueType)
738 il.Emit(Ldarga_S, (byte)0);
741 var lastGetterTypeRef = tSourceRef;
742 for (int j = 0; j < i; j++) {
743 var propTuple = properties [j];
744 var property = propTuple.Item1;
745 var indexerArg = propTuple.Item2;
746 if (indexerArg != null) {
747 if (property.GetMethod.Parameters [0].ParameterType == module.TypeSystem.String)
748 il.Emit(OpCodes.Ldstr, indexerArg);
749 else if (property.GetMethod.Parameters [0].ParameterType == module.TypeSystem.Int32) {
751 if (!int.TryParse(indexerArg, out index))
752 throw new XamlParseException($"Binding: {indexerArg} could not be parsed as an index for a {property.Name}", node as IXmlLineInfo);
753 il.Emit(OpCodes.Ldc_I4, index);
756 if (property.GetMethod.IsVirtual)
757 il.Emit(Callvirt, module.ImportReference(property.GetMethod));
759 il.Emit(Call, module.ImportReference(property.GetMethod));
760 lastGetterTypeRef = property.PropertyType;
762 if (lastGetterTypeRef.IsValueType)
763 il.Emit(Box, module.ImportReference(lastGetterTypeRef));
765 il.Emit(OpCodes.Ret);
766 context.Body.Method.DeclaringType.Methods.Add(partGetter);
767 partGetters.Add(partGetter);
770 var funcObjRef = context.Module.ImportReference(module.ImportReference(("mscorlib", "System", "Func`2")).MakeGenericInstanceType(new [] { tSourceRef, module.TypeSystem.Object }));
771 var tupleRef = context.Module.ImportReference(module.ImportReference(("mscorlib", "System", "Tuple`2")).MakeGenericInstanceType(new [] { funcObjRef, module.TypeSystem.String }));
772 var funcCtor = module.ImportReference(funcObjRef.ResolveCached().GetConstructors().First());
773 funcCtor = funcCtor.MakeGeneric(funcObjRef, new [] { tSourceRef, module.TypeSystem.Object });
774 var tupleCtor = module.ImportReference(tupleRef.ResolveCached().GetConstructors().First());
775 tupleCtor = tupleCtor.MakeGeneric(tupleRef, new [] { funcObjRef, module.TypeSystem.String});
778 // IL_003b: newarr class [mscorlib] System.Tuple`2<class [mscorlib]System.Func`2<class ViewModel,object>,string>
783 // IL_004a: ldftn object class Test::'<Main>m__2'(class ViewModel)
784 // IL_0050: newobj instance void class [mscorlib]System.Func`2<class ViewModel, object>::'.ctor'(object, native int)
785 // IL_005f: ldstr "Model"
786 // IL_0064: newobj instance void class [mscorlib]System.Tuple`2<class [mscorlib]System.Func`2<class ViewModel, object>, string>::'.ctor'(!0, !1)
787 // IL_0069: stelem.ref
792 // IL_0074: ldftn object class Test::'<Main>m__3'(class ViewModel)
793 // IL_007a: newobj instance void class [mscorlib]System.Func`2<class ViewModel, object>::'.ctor'(object, native int)
794 // IL_0089: ldstr "Text"
795 // IL_008e: newobj instance void class [mscorlib]System.Tuple`2<class [mscorlib]System.Func`2<class ViewModel, object>, string>::'.ctor'(!0, !1)
796 // IL_0093: stelem.ref
798 yield return Instruction.Create(OpCodes.Ldc_I4, properties.Count);
799 yield return Instruction.Create(OpCodes.Newarr, tupleRef);
801 for (var i = 0; i < properties.Count; i++) {
802 yield return Instruction.Create(OpCodes.Dup);
803 yield return Instruction.Create(OpCodes.Ldc_I4, i);
804 yield return Instruction.Create(OpCodes.Ldnull);
805 yield return Instruction.Create(OpCodes.Ldftn, partGetters [i]);
806 yield return Instruction.Create(OpCodes.Newobj, module.ImportReference(funcCtor));
807 yield return Instruction.Create(OpCodes.Ldstr, properties [i].Item1.Name);
808 yield return Instruction.Create(OpCodes.Newobj, module.ImportReference(tupleCtor));
809 yield return Instruction.Create(OpCodes.Stelem_Ref);
813 public static IEnumerable<Instruction> SetPropertyValue(VariableDefinition parent, XmlName propertyName, INode valueNode, ILContext context, IXmlLineInfo iXmlLineInfo)
815 var module = context.Body.Method.Module;
816 var localName = propertyName.LocalName;
819 var bpRef = GetBindablePropertyReference(parent, propertyName.NamespaceURI, ref localName, out attached, context, iXmlLineInfo);
821 //If the target is an event, connect
822 if (CanConnectEvent(parent, localName, attached))
824 var instrunctions = ConnectEvent(parent, localName, valueNode, iXmlLineInfo, context);
825 if (null != context.InsOfAddEvent)
827 foreach (var ins in instrunctions)
829 context.InsOfAddEvent.Add(ins);
832 return instrunctions;
835 //If Value is DynamicResource, SetDynamicResource
836 if (CanSetDynamicResource(bpRef, valueNode, context))
837 return SetDynamicResource(parent, bpRef, valueNode as IElementNode, iXmlLineInfo, context);
839 //If Value is a BindingBase and target is a BP, SetBinding
840 if (CanSetBinding(bpRef, valueNode, context))
841 return SetBinding(parent, bpRef, valueNode as IElementNode, iXmlLineInfo, context);
843 //If it's a property, set it
844 if (CanSet(parent, localName, valueNode, context))
845 return Set(parent, localName, valueNode, iXmlLineInfo, context);
847 //If it's a BP, SetValue ()
848 if (CanSetValue(bpRef, attached, valueNode, iXmlLineInfo, context))
849 return SetValue(parent, bpRef, valueNode, iXmlLineInfo, context);
851 //If it's an already initialized property, add to it
852 if (CanAdd(parent, propertyName, valueNode, iXmlLineInfo, context))
853 return Add(parent, propertyName, valueNode, iXmlLineInfo, context);
855 throw new XamlParseException($"No property, bindable property, or event found for '{localName}', or mismatching type between value and property.", iXmlLineInfo);
858 public static IEnumerable<Instruction> GetPropertyValue(VariableDefinition parent, XmlName propertyName, ILContext context, IXmlLineInfo lineInfo, out TypeReference propertyType)
860 var module = context.Body.Method.Module;
861 var localName = propertyName.LocalName;
863 var bpRef = GetBindablePropertyReference(parent, propertyName.NamespaceURI, ref localName, out attached, context, lineInfo);
865 //If it's a BP, GetValue ()
866 if (CanGetValue(parent, bpRef, attached, lineInfo, context, out _))
867 return GetValue(parent, bpRef, lineInfo, context, out propertyType);
869 //If it's a property, set it
870 if (CanGet(parent, localName, context, out _))
871 return Get(parent, localName, lineInfo, context, out propertyType);
873 throw new XamlParseException($"Property {localName} is not found or does not have an accessible getter", lineInfo);
876 static FieldReference GetBindablePropertyReference(VariableDefinition parent, string namespaceURI, ref string localName, out bool attached, ILContext context, IXmlLineInfo iXmlLineInfo)
878 var module = context.Body.Method.Module;
879 TypeReference declaringTypeReference;
881 //If it's an attached BP, update elementType and propertyName
882 var bpOwnerType = parent.VariableType;
883 attached = GetNameAndTypeRef(ref bpOwnerType, namespaceURI, ref localName, context, iXmlLineInfo);
884 var name = $"{localName}Property";
885 FieldReference bpRef = bpOwnerType.GetField(fd => fd.Name == name &&
887 (fd.IsPublic || fd.IsAssembly), out declaringTypeReference);
889 bpRef = module.ImportReference(bpRef.ResolveGenericParameters(declaringTypeReference));
890 bpRef.FieldType = module.ImportReference(bpRef.FieldType);
895 static bool CanConnectEvent(VariableDefinition parent, string localName, bool attached)
897 return !attached && parent.VariableType.GetEvent(ed => ed.Name == localName, out _) != null;
900 static IEnumerable<Instruction> ConnectEvent(VariableDefinition parent, string localName, INode valueNode, IXmlLineInfo iXmlLineInfo, ILContext context)
902 var elementType = parent.VariableType;
903 var module = context.Body.Method.Module;
904 TypeReference eventDeclaringTypeRef;
906 var eventinfo = elementType.GetEvent(ed => ed.Name == localName, out eventDeclaringTypeRef);
911 // IL_0009: ldftn instance void class Tizen.NUI.Xaml.XamlcTests.MyPage::OnButtonClicked(object, class [mscorlib]System.EventArgs)
912 //OR, if the handler is virtual
914 // IL_0009: ldvirtftn instance void class Tizen.NUI.Xaml.XamlcTests.MyPage::OnButtonClicked(object, class [mscorlib]System.EventArgs)
916 // IL_000f: newobj instance void class [mscorlib]System.EventHandler::'.ctor'(object, native int)
917 // IL_0014: callvirt instance void class [Tizen.NUI.Xaml.Core]Tizen.NUI.Xaml.Button::add_Clicked(class [mscorlib]System.EventHandler)
919 var value = ((ValueNode)valueNode).Value;
921 yield return Create(Ldloc, parent);
922 if (context.Root is VariableDefinition)
923 yield return Create(Ldloc, context.Root as VariableDefinition);
924 else if (context.Root is FieldDefinition) {
925 yield return Create(Ldarg_0);
926 yield return Create(Ldfld, context.Root as FieldDefinition);
928 throw new InvalidProgramException();
929 var declaringType = context.Body.Method.DeclaringType;
930 while (declaringType.IsNested)
931 declaringType = declaringType.DeclaringType;
932 var handler = declaringType.AllMethods().FirstOrDefault(md => md.Name == value as string);
934 throw new XamlParseException($"EventHandler \"{value}\" not found in type \"{context.Body.Method.DeclaringType.FullName}\"", iXmlLineInfo);
936 //check if the handler signature matches the Invoke signature;
937 var invoke = module.ImportReference(eventinfo.EventType.ResolveCached().GetMethods().First(md => md.Name == "Invoke"));
938 invoke = invoke.ResolveGenericParameters(eventinfo.EventType, module);
939 if (!handler.ReturnType.InheritsFromOrImplements(invoke.ReturnType))
941 TypeDefinition realType = eventinfo.EventType.ResolveCached();
943 GenericInstanceType genericInstanceType = eventinfo.EventType as GenericInstanceType;
945 if (null != genericInstanceType
946 && genericInstanceType.GenericArguments.Count == realType.GenericParameters.Count)
948 Dictionary<string, TypeReference> dict = new Dictionary<string, TypeReference>();
950 for (int i = 0; i < realType.GenericParameters.Count; i++)
952 string p = realType.GenericParameters[i].Name;
953 TypeReference type = genericInstanceType.GenericArguments[i];
958 if (dict.ContainsKey(invoke.ReturnType.Name))
960 invoke.ReturnType = dict[invoke.ReturnType.Name];
963 for (int i = 0; i < invoke.Parameters.Count; i++)
965 if (dict.ContainsKey(invoke.Parameters[i].ParameterType.Name))
967 invoke.Parameters[i].ParameterType = dict[invoke.Parameters[i].ParameterType.Name];
973 if (!handler.ReturnType.InheritsFromOrImplements(invoke.ReturnType))
974 throw new XamlParseException($"Signature (return type) of EventHandler \"{context.Body.Method.DeclaringType.FullName}.{value}\" doesn't match the event type", iXmlLineInfo);
975 if (invoke.Parameters.Count != handler.Parameters.Count)
976 throw new XamlParseException($"Signature (number of arguments) of EventHandler \"{context.Body.Method.DeclaringType.FullName}.{value}\" doesn't match the event type", iXmlLineInfo);
977 if (!invoke.ContainsGenericParameter)
978 for (var i = 0; i < invoke.Parameters.Count;i++)
979 if (!handler.Parameters[i].ParameterType.InheritsFromOrImplements(invoke.Parameters[i].ParameterType))
980 throw new XamlParseException($"Signature (parameter {i}) of EventHandler \"{context.Body.Method.DeclaringType.FullName}.{value}\" doesn't match the event type", iXmlLineInfo);
982 if (handler.IsVirtual) {
983 yield return Create(Ldarg_0);
984 yield return Create(Ldvirtftn, handler);
986 yield return Create(Ldftn, handler);
988 //FIXME: eventually get the right ctor instead fo the First() one, just in case another one could exists (not even sure it's possible).
989 var ctor = module.ImportReference(eventinfo.EventType.ResolveCached().GetConstructors().First());
990 ctor = ctor.ResolveGenericParameters(eventinfo.EventType, module);
991 yield return Create(Newobj, module.ImportReference(ctor));
992 //Check if the handler has the same signature as the ctor (it should)
993 var adder = module.ImportReference(eventinfo.AddMethod);
994 adder = adder.ResolveGenericParameters(eventDeclaringTypeRef, module);
995 yield return Create(Callvirt, module.ImportReference(adder));
998 static bool CanSetDynamicResource(FieldReference bpRef, INode valueNode, ILContext context)
1002 var elementNode = valueNode as IElementNode;
1003 if (elementNode == null)
1006 VariableDefinition varValue;
1007 if (!context.Variables.TryGetValue(valueNode as IElementNode, out varValue))
1009 return varValue.VariableType.FullName == typeof(DynamicResource).FullName;
1012 static IEnumerable<Instruction> SetDynamicResource(VariableDefinition parent, FieldReference bpRef, IElementNode elementNode, IXmlLineInfo iXmlLineInfo, ILContext context)
1014 var module = context.Body.Method.Module;
1016 yield return Create(Ldloc, parent);
1017 yield return Create(Ldsfld, bpRef);
1018 yield return Create(Ldloc, context.Variables[elementNode]);
1019 yield return Create(Callvirt, module.ImportPropertyGetterReference((XamlCTask.bindingAssemblyName, XamlCTask.bindingInternalNameSpace, "DynamicResource"), propertyName: "Key"));
1020 yield return Create(Callvirt, module.ImportMethodReference((XamlCTask.bindingAssemblyName, XamlCTask.bindingInternalNameSpace, "IDynamicResourceHandler"),
1021 methodName: "SetDynamicResource",
1022 parameterTypes: new[] {
1023 (XamlCTask.bindingAssemblyName, XamlCTask.bindingNameSpace, "BindableProperty"),
1024 ("mscorlib", "System", "String"),
1028 static bool CanSetBinding(FieldReference bpRef, INode valueNode, ILContext context)
1030 var module = context.Body.Method.Module;
1034 var elementNode = valueNode as IElementNode;
1035 if (elementNode == null)
1038 VariableDefinition varValue;
1039 if (!context.Variables.TryGetValue(valueNode as IElementNode, out varValue))
1042 var implicitOperator = varValue.VariableType.GetImplicitOperatorTo(module.ImportReference((XamlCTask.bindingAssemblyName, XamlCTask.bindingNameSpace, "BindingBase")), module);
1043 if (implicitOperator != null)
1046 return varValue.VariableType.InheritsFromOrImplements(module.ImportReference((XamlCTask.bindingAssemblyName, XamlCTask.bindingNameSpace, "BindingBase")));
1049 static IEnumerable<Instruction> SetBinding(VariableDefinition parent, FieldReference bpRef, IElementNode elementNode, IXmlLineInfo iXmlLineInfo, ILContext context)
1051 var module = context.Body.Method.Module;
1052 var varValue = context.Variables [elementNode];
1053 var implicitOperator = varValue.VariableType.GetImplicitOperatorTo(module.ImportReference((XamlCTask.bindingAssemblyName, XamlCTask.bindingNameSpace, "BindingBase")), module);
1055 //TODO: check if parent is a BP
1056 yield return Create(Ldloc, parent);
1057 yield return Create(Ldsfld, bpRef);
1058 yield return Create(Ldloc, varValue);
1059 if (implicitOperator != null)
1060 // IL_000f: call !0 class [Tizen.NUI.Xaml.Core]Tizen.NUI.Xaml.OnPlatform`1<BindingBase>::op_Implicit(class [Tizen.NUI.Xaml.Core]Tizen.NUI.Xaml.OnPlatform`1<!0>)
1061 yield return Create(Call, module.ImportReference(implicitOperator));
1062 yield return Create(Callvirt, module.ImportMethodReference((XamlCTask.bindingAssemblyName, XamlCTask.bindingNameSpace, "BindableObject"),
1063 methodName: "SetBinding",
1064 parameterTypes: new[] {
1065 (XamlCTask.bindingAssemblyName, XamlCTask.bindingNameSpace, "BindableProperty"),
1066 (XamlCTask.bindingAssemblyName, XamlCTask.bindingNameSpace, "BindingBase"),
1070 static bool CanSetValue(FieldReference bpRef, bool attached, INode node, IXmlLineInfo iXmlLineInfo, ILContext context)
1072 var module = context.Body.Method.Module;
1077 var valueNode = node as ValueNode;
1078 if (valueNode != null && valueNode.CanConvertValue(context.Body.Method.Module, bpRef))
1081 var elementNode = node as IElementNode;
1082 if (elementNode == null)
1085 VariableDefinition varValue;
1086 if (!context.Variables.TryGetValue(elementNode, out varValue))
1089 var bpTypeRef = bpRef.GetBindablePropertyType(iXmlLineInfo, module);
1090 // If it's an attached BP, there's no second chance to handle IMarkupExtensions, so we try here.
1091 // Worst case scenario ? InvalidCastException at runtime
1092 if (attached && varValue.VariableType.FullName == "System.Object")
1094 var implicitOperator = varValue.VariableType.GetImplicitOperatorTo(bpTypeRef, module);
1095 if (implicitOperator != null)
1098 //as we're in the SetValue Scenario, we can accept value types, they'll be boxed
1099 if (varValue.VariableType.IsValueType && bpTypeRef.FullName == "System.Object")
1102 return varValue.VariableType.InheritsFromOrImplements(bpTypeRef);
1105 static bool CanGetValue(VariableDefinition parent, FieldReference bpRef, bool attached, IXmlLineInfo iXmlLineInfo, ILContext context, out TypeReference propertyType)
1107 var module = context.Body.Method.Module;
1108 propertyType = null;
1113 if (!parent.VariableType.InheritsFromOrImplements(module.ImportReference((XamlCTask.bindingAssemblyName, XamlCTask.bindingNameSpace, "BindableObject"))))
1116 propertyType = bpRef.GetBindablePropertyType(iXmlLineInfo, module);
1120 static IEnumerable<Instruction> SetValue(VariableDefinition parent, FieldReference bpRef, INode node, IXmlLineInfo iXmlLineInfo, ILContext context)
1122 var valueNode = node as ValueNode;
1123 var elementNode = node as IElementNode;
1124 var module = context.Body.Method.Module;
1127 // IL_0008: ldsfld class [Tizen.NUI.Xaml.Core]Tizen.NUI.Xaml.BindableProperty [Tizen.NUI.Xaml.Core]Tizen.NUI.Xaml.Label::TextProperty
1128 // IL_000d: ldstr "foo"
1129 // IL_0012: callvirt instance void class [Tizen.NUI.Xaml.Core]Tizen.NUI.Xaml.BindableObject::SetValue(class [Tizen.NUI.Xaml.Core]Tizen.NUI.Xaml.BindableProperty, object)
1131 yield return Create(Ldloc, parent);
1132 yield return Create(Ldsfld, bpRef);
1134 if (valueNode != null) {
1135 foreach (var instruction in valueNode.PushConvertedValue(context, bpRef, valueNode.PushServiceProvider(context, bpRef:bpRef), true, false))
1136 yield return instruction;
1137 } else if (elementNode != null) {
1138 var bpTypeRef = bpRef.GetBindablePropertyType(iXmlLineInfo, module);
1139 var varDef = context.Variables[elementNode];
1140 var varType = varDef.VariableType;
1141 var implicitOperator = varDef.VariableType.GetImplicitOperatorTo(bpTypeRef, module);
1142 yield return Create(Ldloc, varDef);
1143 if (implicitOperator != null) {
1144 yield return Create(Call, module.ImportReference(implicitOperator));
1145 varType = module.ImportReference(bpTypeRef);
1147 if (varType.IsValueType)
1148 yield return Create(Box, varType);
1150 yield return Create(Callvirt, module.ImportMethodReference((XamlCTask.bindingAssemblyName, XamlCTask.bindingNameSpace, "BindableObject"),
1151 methodName: "SetValue",
1152 parameterTypes: new[] {
1153 (XamlCTask.bindingAssemblyName, XamlCTask.bindingNameSpace, "BindableProperty"),
1154 ("mscorlib", "System", "Object"),
1158 static IEnumerable<Instruction> GetValue(VariableDefinition parent, FieldReference bpRef, IXmlLineInfo iXmlLineInfo, ILContext context, out TypeReference propertyType)
1160 var module = context.Body.Method.Module;
1161 propertyType = bpRef.GetBindablePropertyType(iXmlLineInfo, module);
1164 Create(Ldloc, parent),
1165 Create(Ldsfld, bpRef),
1166 Create(Callvirt, module.ImportMethodReference((XamlCTask.bindingAssemblyName, XamlCTask.bindingNameSpace, "BindableObject"),
1167 methodName: "GetValue",
1168 parameterTypes: new[] { (XamlCTask.bindingAssemblyName, XamlCTask.bindingNameSpace, "BindableProperty") })),
1172 static bool CanSet(VariableDefinition parent, string localName, INode node, ILContext context)
1174 var module = context.Body.Method.Module;
1175 TypeReference declaringTypeReference;
1176 var property = parent.VariableType.GetProperty(pd => pd.Name == localName, out declaringTypeReference);
1177 if (property == null)
1179 var propertyType = property.ResolveGenericPropertyType(declaringTypeReference, module);
1180 var propertySetter = property.SetMethod;
1181 if (propertySetter == null || !propertySetter.IsPublic || propertySetter.IsStatic)
1184 var valueNode = node as ValueNode;
1185 if (valueNode != null && valueNode.CanConvertValue(context.Body.Method.Module, propertyType, new ICustomAttributeProvider[] { property, propertyType.ResolveCached()}))
1188 var elementNode = node as IElementNode;
1189 if (elementNode == null)
1192 var vardef = context.Variables [elementNode];
1193 var implicitOperator = vardef.VariableType.GetImplicitOperatorTo(propertyType, module);
1195 if (vardef.VariableType.InheritsFromOrImplements(propertyType))
1197 if (implicitOperator != null)
1199 if (propertyType.FullName == "System.Object")
1202 //I'd like to get rid of this condition. This comment used to be //TODO replace latest check by a runtime type check
1203 if (vardef.VariableType.FullName == "System.Object")
1209 static bool CanGet(VariableDefinition parent, string localName, ILContext context, out TypeReference propertyType)
1211 var module = context.Body.Method.Module;
1212 propertyType = null;
1213 TypeReference declaringTypeReference;
1214 var property = parent.VariableType.GetProperty(pd => pd.Name == localName, out declaringTypeReference);
1215 if (property == null)
1217 var propertyGetter = property.GetMethod;
1218 if (propertyGetter == null || !propertyGetter.IsPublic || propertyGetter.IsStatic)
1221 module.ImportReference(parent.VariableType.ResolveCached());
1222 var propertyGetterRef = module.ImportReference(module.ImportReference(propertyGetter).ResolveGenericParameters(declaringTypeReference, module));
1223 propertyGetterRef.ImportTypes(module);
1224 propertyType = propertyGetterRef.ReturnType.ResolveGenericParameters(declaringTypeReference);
1229 static IEnumerable<Instruction> Set(VariableDefinition parent, string localName, INode node, IXmlLineInfo iXmlLineInfo, ILContext context)
1231 var module = context.Body.Method.Module;
1232 TypeReference declaringTypeReference;
1233 var property = parent.VariableType.GetProperty(pd => pd.Name == localName, out declaringTypeReference);
1234 var propertySetter = property.SetMethod;
1237 // IL_0008: ldstr "foo"
1238 // IL_000d: callvirt instance void class [Tizen.NUI.Xaml.Core]Tizen.NUI.Xaml.Label::set_Text(string)
1240 module.ImportReference(parent.VariableType.ResolveCached());
1241 var propertySetterRef = module.ImportReference(module.ImportReference(propertySetter).ResolveGenericParameters(declaringTypeReference, module));
1242 propertySetterRef.ImportTypes(module);
1243 var propertyType = property.ResolveGenericPropertyType(declaringTypeReference, module);
1244 var valueNode = node as ValueNode;
1245 var elementNode = node as IElementNode;
1247 //if it's a value type, load the address so we can invoke methods on it
1248 if (parent.VariableType.IsValueType)
1249 yield return Instruction.Create(OpCodes.Ldloca, parent);
1251 yield return Instruction.Create(OpCodes.Ldloc, parent);
1253 if (valueNode != null) {
1254 foreach (var instruction in valueNode.PushConvertedValue(context, propertyType, new ICustomAttributeProvider [] { property, propertyType.ResolveCached() }, valueNode.PushServiceProvider(context, propertyRef:property), false, true))
1255 yield return instruction;
1256 if (parent.VariableType.IsValueType)
1257 yield return Instruction.Create(OpCodes.Call, propertySetterRef);
1259 yield return Instruction.Create(OpCodes.Callvirt, propertySetterRef);
1260 } else if (elementNode != null) {
1261 var vardef = context.Variables [elementNode];
1262 var implicitOperator = vardef.VariableType.GetImplicitOperatorTo(propertyType, module);
1263 yield return Instruction.Create(OpCodes.Ldloc, vardef);
1264 if (!vardef.VariableType.InheritsFromOrImplements(propertyType) && implicitOperator != null) {
1265 // IL_000f: call !0 class [Tizen.NUI.Xaml.Core]Tizen.NUI.Xaml.OnPlatform`1<bool>::op_Implicit(class [Tizen.NUI.Xaml.Core]Tizen.NUI.Xaml.OnPlatform`1<!0>)
1266 yield return Instruction.Create(OpCodes.Call, module.ImportReference(implicitOperator));
1267 } else if (!vardef.VariableType.IsValueType && propertyType.IsValueType)
1268 yield return Instruction.Create(OpCodes.Unbox_Any, module.ImportReference(propertyType));
1269 else if (vardef.VariableType.IsValueType && propertyType.FullName == "System.Object")
1270 yield return Instruction.Create(OpCodes.Box, vardef.VariableType);
1271 if (parent.VariableType.IsValueType)
1272 yield return Instruction.Create(OpCodes.Call, propertySetterRef);
1274 yield return Instruction.Create(OpCodes.Callvirt, propertySetterRef);
1278 static IEnumerable<Instruction> Get(VariableDefinition parent, string localName, IXmlLineInfo iXmlLineInfo, ILContext context, out TypeReference propertyType)
1280 var module = context.Body.Method.Module;
1281 var property = parent.VariableType.GetProperty(pd => pd.Name == localName, out var declaringTypeReference);
1282 var propertyGetter = property.GetMethod;
1284 module.ImportReference(parent.VariableType.ResolveCached());
1285 var propertyGetterRef = module.ImportReference(module.ImportReference(propertyGetter).ResolveGenericParameters(declaringTypeReference, module));
1286 propertyGetterRef.ImportTypes(module);
1287 propertyType = propertyGetterRef.ReturnType.ResolveGenericParameters(declaringTypeReference);
1289 if (parent.VariableType.IsValueType)
1291 Instruction.Create(OpCodes.Ldloca, parent),
1292 Instruction.Create(OpCodes.Call, propertyGetterRef),
1296 Instruction.Create(OpCodes.Ldloc, parent),
1297 Instruction.Create(OpCodes.Callvirt, propertyGetterRef),
1301 static bool CanAdd(VariableDefinition parent, XmlName propertyName, INode node, IXmlLineInfo lineInfo, ILContext context)
1303 var module = context.Body.Method.Module;
1304 var localName = propertyName.LocalName;
1306 var bpRef = GetBindablePropertyReference(parent, propertyName.NamespaceURI, ref localName, out attached, context, lineInfo);
1307 TypeReference propertyType;
1309 if ( !CanGetValue(parent, bpRef, attached, null, context, out propertyType)
1310 && !CanGet(parent, localName, context, out propertyType))
1313 //TODO check md.Parameters[0] type
1314 var adderTuple = propertyType.GetMethods(md => md.Name == "Add" && md.Parameters.Count == 1, module).FirstOrDefault();
1315 if (adderTuple == null)
1321 static Dictionary<VariableDefinition, IList<string>> resourceNamesInUse = new Dictionary<VariableDefinition, IList<string>>();
1322 static bool CanAddToResourceDictionary(VariableDefinition parent, TypeReference collectionType, IElementNode node, IXmlLineInfo lineInfo, ILContext context)
1324 if ( collectionType.FullName != "Tizen.NUI.Binding.ResourceDictionary"
1325 && collectionType.ResolveCached().BaseType?.FullName != "Tizen.NUI.Binding.ResourceDictionary")
1329 if (node.Properties.ContainsKey(XmlName.xKey)) {
1330 var key = (node.Properties[XmlName.xKey] as ValueNode).Value as string;
1331 if (!resourceNamesInUse.TryGetValue(parent, out var names))
1332 resourceNamesInUse[parent] = (names = new List<string>());
1333 if (names.Contains(key))
1334 throw new XamlParseException($"A resource with the key '{key}' is already present in the ResourceDictionary.", lineInfo);
1339 //is there a RD.Add() overrides that accepts this ?
1340 var nodeTypeRef = context.Variables[node].VariableType;
1341 var module = context.Body.Method.Module;
1342 if (module.ImportMethodReference((XamlCTask.bindingAssemblyName, XamlCTask.bindingNameSpace, "ResourceDictionary"),
1344 parameterTypes: new[] { (nodeTypeRef.Scope.Name, nodeTypeRef.Namespace, nodeTypeRef.Name) }) != null)
1347 throw new XamlParseException("resources in ResourceDictionary require a x:Key attribute", lineInfo);
1350 static IEnumerable<Instruction> Add(VariableDefinition parent, XmlName propertyName, INode node, IXmlLineInfo iXmlLineInfo, ILContext context)
1352 var module = context.Body.Method.Module;
1353 var elementNode = node as IElementNode;
1354 var vardef = context.Variables [elementNode];
1356 TypeReference propertyType;
1357 foreach (var instruction in GetPropertyValue(parent, propertyName, context, iXmlLineInfo, out propertyType))
1358 yield return instruction;
1360 if (CanAddToResourceDictionary(parent, propertyType, elementNode, iXmlLineInfo, context)) {
1361 foreach (var instruction in AddToResourceDictionary(elementNode, iXmlLineInfo, context))
1362 yield return instruction;
1366 var adderTuple = propertyType.GetMethods(md => md.Name == "Add" && md.Parameters.Count == 1, module).FirstOrDefault();
1367 var adderRef = module.ImportReference(adderTuple.Item1);
1368 adderRef = module.ImportReference(adderRef.ResolveGenericParameters(adderTuple.Item2, module));
1369 var childType = GetParameterType(adderRef.Parameters[0]);
1370 var implicitOperator = vardef.VariableType.GetImplicitOperatorTo(childType, module);
1372 yield return Instruction.Create(OpCodes.Ldloc, vardef);
1373 if (implicitOperator != null)
1374 yield return Instruction.Create(OpCodes.Call, module.ImportReference(implicitOperator));
1375 if (implicitOperator == null && vardef.VariableType.IsValueType && !childType.IsValueType)
1376 yield return Instruction.Create(OpCodes.Box, vardef.VariableType);
1377 yield return Instruction.Create(OpCodes.Callvirt, adderRef);
1378 if (adderRef.ReturnType.FullName != "System.Void")
1379 yield return Instruction.Create(OpCodes.Pop);
1382 static IEnumerable<Instruction> AddToResourceDictionary(IElementNode node, IXmlLineInfo lineInfo, ILContext context)
1384 var module = context.Body.Method.Module;
1386 if (node.Properties.ContainsKey(XmlName.xKey)) {
1387 // IL_0014: ldstr "key"
1388 // IL_0019: ldstr "foo"
1389 // IL_001e: callvirt instance void class [Tizen.NUI.Xaml.Core]Tizen.NUI.Xaml.ResourceDictionary::Add(string, object)
1390 yield return Create(Ldstr, (node.Properties[XmlName.xKey] as ValueNode).Value as string);
1391 var varDef = context.Variables[node];
1392 yield return Create(Ldloc, varDef);
1393 if (varDef.VariableType.IsValueType)
1394 yield return Create(Box, module.ImportReference(varDef.VariableType));
1395 yield return Create(Callvirt, module.ImportMethodReference((XamlCTask.bindingAssemblyName, XamlCTask.bindingNameSpace, "ResourceDictionary"),
1397 parameterTypes: new[] {
1398 ("mscorlib", "System", "String"),
1399 ("mscorlib", "System", "Object"),
1404 var nodeTypeRef = context.Variables[node].VariableType;
1405 yield return Create(Ldloc, context.Variables[node]);
1406 yield return Create(Callvirt, module.ImportMethodReference((XamlCTask.bindingAssemblyName, XamlCTask.bindingNameSpace, "ResourceDictionary"),
1408 parameterTypes: new[] { (nodeTypeRef.Scope.Name, nodeTypeRef.Namespace, nodeTypeRef.Name) }));
1412 public static TypeReference GetParameterType(ParameterDefinition param)
1414 if (!param.ParameterType.IsGenericParameter)
1415 return param.ParameterType;
1416 var type = (param.Method as MethodReference).DeclaringType as GenericInstanceType;
1417 return type.GenericArguments [0];
1420 static bool GetNameAndTypeRef(ref TypeReference elementType, string namespaceURI, ref string localname,
1421 ILContext context, IXmlLineInfo lineInfo)
1423 var dotIdx = localname.IndexOf('.');
1426 var typename = localname.Substring(0, dotIdx);
1427 localname = localname.Substring(dotIdx + 1);
1428 elementType = new XmlType(namespaceURI, typename, null).GetTypeReference(XmlTypeExtensions.ModeOfGetType.Both, context.Body.Method.Module, lineInfo);
1434 static void SetDataTemplate(IElementNode parentNode, ElementNode node, ILContext parentContext,
1435 IXmlLineInfo xmlLineInfo)
1437 var parentVar = parentContext.Variables[parentNode];
1438 //Push the DataTemplate to the stack, for setting the template
1439 parentContext.IL.Emit(OpCodes.Ldloc, parentVar);
1441 //Create nested class
1442 // .class nested private auto ansi sealed beforefieldinit '<Main>c__AnonStorey0'
1443 // extends [mscorlib]System.Object
1446 var module = parentContext.Module;
1447 var anonType = new TypeDefinition(
1449 "_" + parentContext.Body.Method.Name + "_anonXamlCDataTemplate_" + dtcount++,
1450 TypeAttributes.BeforeFieldInit |
1451 TypeAttributes.Sealed |
1452 TypeAttributes.NestedPrivate) {
1453 BaseType = module.TypeSystem.Object,
1454 CustomAttributes = {
1455 new CustomAttribute (module.ImportCtorReference(("mscorlib", "System.Runtime.CompilerServices", "CompilerGeneratedAttribute"), parameterTypes: null)),
1459 parentContext.Body.Method.DeclaringType.NestedTypes.Add(anonType);
1460 var ctor = anonType.AddDefaultConstructor();
1462 var loadTemplate = new MethodDefinition("LoadDataTemplate",
1463 MethodAttributes.Assembly | MethodAttributes.HideBySig,
1464 module.TypeSystem.Object);
1465 loadTemplate.Body.InitLocals = true;
1466 anonType.Methods.Add(loadTemplate);
1468 var parentValues = new FieldDefinition("parentValues", FieldAttributes.Assembly, module.ImportArrayReference(("mscorlib", "System", "Object")));
1469 anonType.Fields.Add(parentValues);
1471 TypeReference rootType = null;
1472 var vdefRoot = parentContext.Root as VariableDefinition;
1473 if (vdefRoot != null)
1474 rootType = vdefRoot.VariableType;
1475 var fdefRoot = parentContext.Root as FieldDefinition;
1476 if (fdefRoot != null)
1477 rootType = fdefRoot.FieldType;
1479 var root = new FieldDefinition("root", FieldAttributes.Assembly, rootType);
1480 anonType.Fields.Add(root);
1482 //Fill the loadTemplate Body
1483 var templateIl = loadTemplate.Body.GetILProcessor();
1484 templateIl.Emit(OpCodes.Nop);
1485 var templateContext = new ILContext(templateIl, loadTemplate.Body, null, module, parentValues)
1489 node.Accept(new CreateObjectVisitor(templateContext), null);
1490 node.Accept(new SetNamescopesAndRegisterNamesVisitor(templateContext), null);
1491 node.Accept(new SetFieldVisitor(templateContext), null);
1492 node.Accept(new SetResourcesVisitor(templateContext), null);
1493 node.Accept(new SetPropertiesVisitor(templateContext, stopOnResourceDictionary: true), null);
1495 templateIl.Emit(OpCodes.Ldloc, templateContext.Variables[node]);
1496 templateIl.Emit(OpCodes.Ret);
1498 //Instanciate nested class
1499 var parentIl = parentContext.IL;
1500 parentIl.Emit(OpCodes.Newobj, ctor);
1502 //Copy required local vars
1503 parentIl.Emit(OpCodes.Dup); //Duplicate the nestedclass instance
1504 parentIl.Append(node.PushParentObjectsArray(parentContext));
1505 parentIl.Emit(OpCodes.Stfld, parentValues);
1506 parentIl.Emit(OpCodes.Dup); //Duplicate the nestedclass instance
1507 if (parentContext.Root is VariableDefinition)
1508 parentIl.Emit(OpCodes.Ldloc, parentContext.Root as VariableDefinition);
1509 else if (parentContext.Root is FieldDefinition)
1511 parentIl.Emit(OpCodes.Ldarg_0);
1512 parentIl.Emit(OpCodes.Ldfld, parentContext.Root as FieldDefinition);
1515 throw new InvalidProgramException();
1516 parentIl.Emit(OpCodes.Stfld, root);
1519 parentIl.Emit(Ldftn, loadTemplate);
1520 parentIl.Emit(Newobj, module.ImportCtorReference(("mscorlib", "System", "Func`1"),
1521 classArguments: new[] { ("mscorlib", "System", "Object") },
1524 var setterRef = module.ImportPropertySetterReference((XamlCTask.bindingAssemblyName, XamlCTask.bindingNameSpace, "IDataTemplate"), propertyName: "LoadTemplate");
1525 parentContext.IL.Emit(OpCodes.Callvirt, setterRef);
1527 loadTemplate.Body.Optimize();
1530 bool TrySetRuntimeName(XmlName propertyName, VariableDefinition variableDefinition, ValueNode node)
1532 if (propertyName != XmlName.xName)
1535 var attributes = variableDefinition.VariableType.ResolveCached()
1536 .CustomAttributes.Where(attribute => attribute.AttributeType.FullName == "Tizen.NUI.Xaml.RuntimeNamePropertyAttribute").ToList();
1538 if (!attributes.Any())
1541 var runTimeName = attributes[0].ConstructorArguments[0].Value as string;
1543 if (string.IsNullOrEmpty(runTimeName))
1546 Context.IL.Append(SetPropertyValue(variableDefinition, new XmlName("", runTimeName), node, Context, node));