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;
27 using Tizen.NUI.EXaml;
29 using Tizen.NUI.Xaml.Build.Tasks;
30 using Tizen.NUI.Xaml.Core.XamlC;
31 using static Mono.Cecil.Cil.Instruction;
32 using static Mono.Cecil.Cil.OpCodes;
34 namespace Tizen.NUI.EXaml.Build.Tasks
36 class EXamlSetPropertiesVisitor : IXamlNodeVisitor
39 static int typedBindingCount;
41 static readonly IList<XmlName> skips = new List<XmlName>
44 XmlName.xTypeArguments,
46 XmlName.xFactoryMethod,
51 public EXamlSetPropertiesVisitor(EXamlContext context, bool stopOnResourceDictionary = false)
54 Module = context.Module;
55 StopOnResourceDictionary = stopOnResourceDictionary;
58 public EXamlContext Context { get; }
59 public bool StopOnResourceDictionary { get; }
60 public TreeVisitingMode VisitingMode => TreeVisitingMode.BottomUp;
61 public bool StopOnDataTemplate => true;
62 public bool VisitNodeOnDataTemplate => true;
63 public bool SkipChildren(INode node, INode parentNode) => false;
65 public bool IsResourceDictionary(ElementNode node)
67 var parentVar = Context.Values[node] as EXamlCreateObject;
68 return null != parentVar
70 (parentVar.GetType().FullName == "Tizen.NUI.Binding.ResourceDictionary"
71 || parentVar.GetType().Resolve().BaseType?.FullName == "Tizen.NUI.Binding.ResourceDictionary");
74 ModuleDefinition Module { get; }
76 public void Visit(ValueNode node, INode parentNode)
78 //TODO support Label text as element
80 if (!TryGetPropertyName(node, parentNode, out propertyName))
82 if (!IsCollectionItem(node, parentNode))
84 string contentProperty;
85 if (!Context.Variables.ContainsKey((IElementNode)parentNode))
87 var parentVar = Context.Variables[(IElementNode)parentNode];
88 if ((contentProperty = GetContentProperty(parentVar.VariableType)) != null)
89 propertyName = new XmlName(((IElementNode)parentNode).NamespaceURI, contentProperty);
94 if (TrySetRuntimeName(propertyName, Context.Values[parentNode] as EXamlCreateObject, node))
96 if (skips.Contains(propertyName))
98 if (parentNode is IElementNode && ((IElementNode)parentNode).SkipProperties.Contains (propertyName))
100 if (propertyName.Equals(XamlParser.McUri, "Ignorable"))
103 SetPropertyValue(Context.Values[parentNode] as EXamlCreateObject, propertyName, node, Context, node);
106 public void Visit(MarkupNode node, INode parentNode)
110 public void Visit(ElementNode node, INode parentNode)
112 XmlName propertyName = XmlName.Empty;
114 //Simplify ListNodes with single elements
115 var pList = parentNode as ListNode;
118 (pList.CollectionItems.Count == 1
120 "XamlResources" == pList.XmlName.LocalName))
122 propertyName = pList.XmlName;
123 parentNode = parentNode.Parent;
126 if ((propertyName != XmlName.Empty || TryGetPropertyName(node, parentNode, out propertyName)) && skips.Contains(propertyName))
129 if (propertyName == XmlName._CreateContent
131 parentNode is ElementNode parentElementNode)
133 SetDataTemplate(parentElementNode, node, Context, node);
137 //if this node is an IMarkupExtension, invoke ProvideValue() and replace the variable
138 var vardef = Context.Values[node];
140 var localName = propertyName.LocalName;
141 TypeReference declaringTypeReference = null;
143 PropertyDefinition propertyRef = null;
144 if (parentNode is IElementNode && propertyName != XmlName.Empty) {
145 propertyRef = Context.Variables [(IElementNode)parentNode].VariableType.GetProperty(pd => pd.Name == localName, out declaringTypeReference);
148 if (vardef is EXamlCreateObject)
150 var realValue = ProvideValue(vardef as EXamlCreateObject, Context, Module, node, propertyRef: propertyRef, propertyDeclaringTypeRef: declaringTypeReference);
151 if (null != realValue && vardef != realValue)
153 (vardef as EXamlCreateObject).IsValid = false;
154 Context.Values[node] = realValue;
159 if (propertyName != XmlName.Empty) {
160 if (skips.Contains(propertyName))
162 if (parentNode is IElementNode && ((IElementNode)parentNode).SkipProperties.Contains (propertyName))
165 SetPropertyValue(Context.Values[parentNode] as EXamlCreateObject, propertyName, node, Context, node);
167 else if (IsCollectionItem(node, parentNode) && parentNode is IElementNode) {
168 var parentVar = Context.Values[parentNode] as EXamlCreateObject;
169 string contentProperty;
171 bool isAdded = false;
173 if (CanAddToResourceDictionary(parentVar, parentVar.GetType(), node, node, Context))
175 var keyName = (node.Properties[XmlName.xKey] as ValueNode).Value as string;
176 new EXamlAddToResourceDictionary(Context, parentVar, keyName, Context.Values[node]);
180 // Collection element, implicit content, or implicit collection element.
181 if (!isAdded && parentVar.GetType().GetMethods(md => md.Name == "Add" && md.Parameters.Count == 1, Module).Any())
183 var elementType = parentVar.GetType();
184 var paramType = Context.Variables[node].VariableType;
186 foreach (var adderTuple in elementType.GetMethods(md => md.Name == "Add" && md.Parameters.Count == 1, Module))
188 var adderRef = Module.ImportReference(adderTuple.Item1);
189 adderRef = Module.ImportReference(adderRef.ResolveGenericParameters(adderTuple.Item2, Module));
191 if (IsAddMethodOfCollection(Module, adderRef.Resolve()))
193 new EXamlAddToCollectionInstance(Context, parentVar, Context.Values[node]);
197 else if (paramType.InheritsFromOrImplements(adderTuple.Item1.Parameters[0].ParameterType.FullName))
199 new EXamlAddObject(Context, parentVar, Context.Values[node], adderRef.Resolve());
206 if (!isAdded && (contentProperty = GetContentProperty(parentVar.GetType())) != null)
208 var name = new XmlName(node.NamespaceURI, contentProperty);
209 if (skips.Contains(name))
211 if (parentNode is IElementNode && ((IElementNode)parentNode).SkipProperties.Contains(propertyName))
214 SetPropertyValue(Context.Values[parentNode] as EXamlCreateObject, name, node, Context, node);
220 throw new XamlParseException($"Can not set the content of {((IElementNode)parentNode).XmlType.Name} as it doesn't have a ContentPropertyAttribute", node);
223 else if (IsCollectionItem(node, parentNode) && parentNode is ListNode)
225 var parentList = (ListNode)parentNode;
226 if (skips.Contains(parentList.XmlName))
228 if (parentNode is IElementNode && ((IElementNode)parentNode).SkipProperties.Contains (propertyName))
231 var parent = Context.Variables[((IElementNode)parentNode.Parent)];
232 var elementType = parent.VariableType;
233 localName = parentList.XmlName.LocalName;
235 if (Context.Values[parentList.Parent] is EXamlCreateObject)
237 if (localName.Contains('.'))
239 int index = localName.LastIndexOf('.');
240 localName = localName.Substring(index + 1);
243 var getObjectByProperty = new EXamlGetObjectByProperty(Context, Context.Values[parentList.Parent] as EXamlCreateObject, localName);
244 new EXamlAddToCollectionProperty(Context, getObjectByProperty, Context.Values[node]);
249 public void Visit(RootNode node, INode parentNode)
253 public void Visit(ListNode node, INode parentNode)
257 private static MethodDefinition addDefOfCollection;
258 private static bool IsAddMethodOfCollection(ModuleDefinition module, MethodDefinition methodDef)
260 return module.ImportReference(typeof(List<string>)).InheritsFromOrImplements(methodDef.DeclaringType);
263 public static bool TryGetPropertyName(INode node, INode parentNode, out XmlName name)
265 name = default(XmlName);
266 var parentElement = parentNode as IElementNode;
267 if (parentElement == null)
269 foreach (var kvp in parentElement.Properties)
271 if (kvp.Value != node)
279 static bool IsCollectionItem(INode node, INode parentNode)
281 var parentList = parentNode as IListNode;
282 if (parentList == null)
284 return parentList.CollectionItems.Contains(node);
287 internal static string GetContentProperty(TypeReference typeRef)
289 var typeDef = typeRef.ResolveCached();
290 var attributes = typeDef.CustomAttributes;
292 attributes.FirstOrDefault(cad => ContentPropertyAttribute.ContentPropertyTypes.Contains(cad.AttributeType.FullName));
294 return attr.ConstructorArguments[0].Value as string;
295 if (typeDef.BaseType == null)
297 return GetContentProperty(typeDef.BaseType);
300 public static object ProvideValue(EXamlCreateObject instance, EXamlContext context,
301 ModuleDefinition module, ElementNode node,
302 PropertyReference propertyRef = null, TypeReference propertyDeclaringTypeRef = null)
304 GenericInstanceType markupExtension;
305 IList<TypeReference> genericArguments;
306 if (instance.GetType().FullName == "Tizen.NUI.Xaml.Build.Tasks.ArrayExtension")
308 var nodeValue = context.Values[node] as EXamlCreateObject;
310 if (nodeValue?.Instance is Xaml.Build.Tasks.ArrayExtension arrayExtension)
312 return arrayExtension.ProvideValue(node, module, context);
315 else if (instance.GetType().ImplementsGenericInterface("Tizen.NUI.Xaml.IMarkupExtension`1", out markupExtension, out genericArguments))
317 var nodeValue = context.Values[node] as EXamlCreateObject;
318 if (nodeValue?.Instance is BindingExtension)
320 var newValue = (nodeValue.Instance as BindingExtension).ProvideValue(context, module);
323 else if (nodeValue?.Instance is DynamicResourceExtension)
325 var dynamicResExtension = nodeValue.Instance as DynamicResourceExtension;
326 var newValue = dynamicResExtension.ProvideValue();
327 var newTypeRef = module.ImportReference(newValue.GetType());
328 return new EXamlCreateObject(context, newValue, newTypeRef, new object[] { dynamicResExtension.Key });
333 var nodeValue = context.Values[node] as EXamlCreateObject;
334 if (null != nodeValue)
336 if (nodeValue.GetType().ImplementsInterface(module.ImportReference((XamlTask.xamlAssemblyName, XamlTask.xamlNameSpace, "IMarkupExtension"))))
338 var acceptEmptyServiceProvider = instance.GetType().GetCustomAttribute(module, (XamlTask.xamlAssemblyName, XamlTask.xamlNameSpace, "AcceptEmptyServiceProviderAttribute")) != null;
339 if (nodeValue.Instance is ReferenceExtension)
341 var newValue = (nodeValue.Instance as ReferenceExtension).ProvideValue(context);
344 else if (nodeValue.Instance is StaticResourceExtension)
346 var newValue = (nodeValue.Instance as StaticResourceExtension).ProvideValue(context);
350 else if (nodeValue.GetType().ImplementsInterface(module.ImportReference((XamlTask.xamlAssemblyName, XamlTask.xamlNameSpace, "IValueProvider"))))
352 //var acceptEmptyServiceProvider = context.Variables[node].VariableType.GetCustomAttribute(module, (XamlCTask.xamlAssemblyName, XamlCTask.xamlNameSpace, "AcceptEmptyServiceProviderAttribute")) != null;
353 //var valueProviderType = context.Variables[node].VariableType;
354 ////If the IValueProvider has a ProvideCompiledAttribute that can be resolved, shortcut this
355 //var compiledValueProviderName = valueProviderType?.GetCustomAttribute(module, (XamlCTask.xamlAssemblyName, XamlCTask.xamlNameSpace, "ProvideCompiledAttribute"))?.ConstructorArguments?[0].Value as string;
356 //Type compiledValueProviderType;
357 //if (compiledValueProviderName != null && (compiledValueProviderType = Type.GetType(compiledValueProviderName)) != null)
359 // var compiledValueProvider = Activator.CreateInstance(compiledValueProviderType);
360 // var cProvideValue = typeof(ICompiledValueProvider).GetMethods().FirstOrDefault(md => md.Name == "ProvideValue");
361 // var instructions = (IEnumerable<Instruction>)cProvideValue.Invoke(compiledValueProvider, new object[] {
366 //foreach (var i in instructions)
373 //instance.VariableDefinition = new VariableDefinition(module.TypeSystem.Object);
374 //yield return Create(Ldloc, context.Variables[node]);
375 //if (acceptEmptyServiceProvider)
376 // yield return Create(Ldnull);
378 // foreach (var instruction in node.PushServiceProvider(context, bpRef, propertyRef, propertyDeclaringTypeRef))
379 // yield return instruction;
380 //yield return Create(Callvirt, module.ImportMethodReference((XamlCTask.xamlAssemblyName, XamlCTask.xamlNameSpace, "IValueProvider"),
381 // methodName: "ProvideValue",
382 // parameterTypes: new[] { ("System.ComponentModel", "System", "IServiceProvider") }));
383 //yield return Create(Stloc, instance.VariableDefinition);
389 static IList<Tuple<PropertyDefinition, string>> ParsePath(string path, TypeReference tSourceRef, IXmlLineInfo lineInfo, ModuleDefinition module)
391 if (string.IsNullOrWhiteSpace(path))
393 path = path.Trim(' ', '.'); //trim leading or trailing dots
394 var parts = path.Split(new [] { '.' }, StringSplitOptions.RemoveEmptyEntries);
395 var properties = new List<Tuple<PropertyDefinition, string>>();
397 var previousPartTypeRef = tSourceRef;
399 foreach (var part in parts) {
401 string indexArg = null;
402 var lbIndex = p.IndexOf('[');
404 var rbIndex = p.LastIndexOf(']');
406 throw new XamlParseException("Binding: Indexer did not contain closing bracket", lineInfo);
408 var argLength = rbIndex - lbIndex - 1;
410 throw new XamlParseException("Binding: Indexer did not contain arguments", lineInfo);
412 indexArg = p.Substring(lbIndex + 1, argLength).Trim();
413 if (indexArg.Length == 0)
414 throw new XamlParseException("Binding: Indexer did not contain arguments", lineInfo);
416 p = p.Substring(0, lbIndex);
421 var property = previousPartTypeRef.GetProperty(pd => pd.Name == p && pd.GetMethod != null && pd.GetMethod.IsPublic, out _)
422 ?? throw new XamlParseException($"Binding: Property '{p}' not found on '{previousPartTypeRef}'", lineInfo);
423 properties.Add(new Tuple<PropertyDefinition, string>(property,null));
424 previousPartTypeRef = property.PropertyType;
426 if (indexArg != null) {
427 var defaultMemberAttribute = previousPartTypeRef.GetCustomAttribute(module, ("mscorlib", "System.Reflection", "DefaultMemberAttribute"));
428 var indexerName = defaultMemberAttribute?.ConstructorArguments?.FirstOrDefault().Value as string ?? "Item";
429 var indexer = previousPartTypeRef.GetProperty(pd => pd.Name == indexerName && pd.GetMethod != null && pd.GetMethod.IsPublic, out _);
430 properties.Add(new Tuple<PropertyDefinition, string>(indexer, indexArg));
431 if (indexer.PropertyType != module.TypeSystem.String && indexer.PropertyType != module.TypeSystem.Int32)
432 throw new XamlParseException($"Binding: Unsupported indexer index type: {indexer.PropertyType.FullName}", lineInfo);
433 previousPartTypeRef = indexer.PropertyType;
439 public static void SetPropertyValue(EXamlCreateObject parent, XmlName propertyName, INode valueNode, EXamlContext context, IXmlLineInfo iXmlLineInfo)
441 var module = context.Module;
442 var localName = propertyName.LocalName;
445 var bpRef = GetBindablePropertyReference(parent, propertyName.NamespaceURI, ref localName, out attached, context, iXmlLineInfo);
447 //If the target is an event, connect
448 if (CanConnectEvent(parent, localName, attached))
450 ConnectEvent(parent, localName, valueNode, iXmlLineInfo, context);
452 //If Value is DynamicResource, SetDynamicResource
453 else if (CanSetDynamicResource(bpRef, valueNode, context))
455 SetDynamicResource(parent, bpRef, valueNode as IElementNode, iXmlLineInfo, context);
457 //If Value is a BindingBase and target is a BP, SetBinding
458 else if (CanSetBinding(bpRef, valueNode, context))
460 SetBinding(parent, bpRef, valueNode as IElementNode, iXmlLineInfo, context);
462 //If it's a property, set it
463 else if (CanSet(parent, localName, valueNode, context))
465 Set(parent, localName, valueNode, iXmlLineInfo, context);
467 //If it's a BP, SetValue ()
468 else if (CanSetValue(bpRef, attached, valueNode, iXmlLineInfo, context))
470 SetValue(parent, bpRef, valueNode, iXmlLineInfo, context);
472 //If it's an already initialized property, add to it
473 else if (CanAdd(parent, propertyName, valueNode, iXmlLineInfo, context))
475 Add(parent, localName, valueNode, iXmlLineInfo, context);
479 throw new XamlParseException($"No property, bindable property, or event found for '{localName}', or mismatching type between value and property.", iXmlLineInfo);
483 //public static IEnumerable<Instruction> GetPropertyValue(EXamlCreateObject parent, XmlName propertyName, ILContext context, IXmlLineInfo lineInfo, out TypeReference propertyType)
485 // var module = context.Module;
486 // var localName = propertyName.LocalName;
488 // var bpRef = GetBindablePropertyReference(parent, propertyName.NamespaceURI, ref localName, out attached, context, lineInfo);
490 // //If it's a BP, GetValue ()
491 // if (CanGetValue(parent, bpRef, attached, lineInfo, context, out _))
492 // return GetValue(parent, bpRef, lineInfo, context, out propertyType);
494 // //If it's a property, set it
495 // if (CanGet(parent, localName, context, out _))
496 // return Get(parent, localName, lineInfo, context, out propertyType);
498 // throw new XamlParseException($"Property {localName} is not found or does not have an accessible getter", lineInfo);
501 static MemberReference GetBindablePropertyReference(EXamlCreateObject parent, string namespaceURI, ref string localName, out bool attached, EXamlContext context, IXmlLineInfo iXmlLineInfo)
503 var module = context.Module;
504 TypeReference declaringTypeReference;
506 //If it's an attached BP, update elementType and propertyName
507 var bpOwnerType = parent.GetType();
508 attached = GetNameAndTypeRef(ref bpOwnerType, namespaceURI, ref localName, context, iXmlLineInfo);
509 var name = $"{localName}Property";
511 PropertyReference prRef = bpOwnerType.GetProperty(p => p.Name == name &&
512 p.GetMethod.IsStatic && p.GetMethod.IsPublic,
513 out declaringTypeReference);
517 //prRef = module.ImportReference(prRef.ResolveGenericParameters(declaringTypeReference));
518 prRef.PropertyType = module.ImportReference(prRef.PropertyType);
522 FieldReference bpRef = bpOwnerType.GetField(fd => fd.Name == name &&
524 (fd.IsPublic || fd.IsAssembly), out declaringTypeReference);
526 bpRef = module.ImportReference(bpRef.ResolveGenericParameters(declaringTypeReference));
527 bpRef.FieldType = module.ImportReference(bpRef.FieldType);
532 static bool CanConnectEvent(EXamlCreateObject parent, string localName, bool attached)
534 return !attached && parent.GetType().GetEvent(ed => ed.Name == localName, out _) != null;
537 static void ConnectEvent(EXamlCreateObject parent, string localName, INode valueNode, IXmlLineInfo iXmlLineInfo, EXamlContext context)
539 //Fang: Need to deal connect event
540 var elementType = parent.GetType();
541 var module = context.Module;
542 TypeReference eventDeclaringTypeRef;
544 var eventinfo = elementType.GetEvent(ed => ed.Name == localName, out eventDeclaringTypeRef);
549 // IL_0009: ldftn instance void class Tizen.NUI.Xaml.XamlcTests.MyPage::OnButtonClicked(object, class [mscorlib]System.EventArgs)
550 //OR, if the handler is virtual
552 // IL_0009: ldvirtftn instance void class Tizen.NUI.Xaml.XamlcTests.MyPage::OnButtonClicked(object, class [mscorlib]System.EventArgs)
554 // IL_000f: newobj instance void class [mscorlib]System.EventHandler::'.ctor'(object, native int)
555 // IL_0014: callvirt instance void class [Tizen.NUI.Xaml.Core]Tizen.NUI.Xaml.Button::add_Clicked(class [mscorlib]System.EventHandler)
557 var value = ((ValueNode)valueNode).Value;
559 //if (context.Root is VariableDefinition)
560 // yield return Create(Ldloc, context.Root as VariableDefinition);
561 //else if (context.Root is FieldDefinition)
563 // yield return Create(Ldarg_0);
564 // yield return Create(Ldfld, context.Root as FieldDefinition);
567 // throw new InvalidProgramException();
568 var declaringType = context.Type;
569 while (declaringType.IsNested)
570 declaringType = declaringType.DeclaringType;
571 var handler = declaringType.AllMethods().FirstOrDefault(md => md.Name == value as string);
573 //check if the handler signature matches the Invoke signature;
574 var invoke = module.ImportReference(eventinfo.EventType.ResolveCached().GetMethods().First(md => md.Name == "Invoke"));
575 invoke = invoke.ResolveGenericParameters(eventinfo.EventType, module);
576 if (!handler.ReturnType.InheritsFromOrImplements(invoke.ReturnType))
578 TypeDefinition realType = eventinfo.EventType.ResolveCached();
580 GenericInstanceType genericInstanceType = eventinfo.EventType as GenericInstanceType;
582 if (null != genericInstanceType
583 && genericInstanceType.GenericArguments.Count == realType.GenericParameters.Count)
585 Dictionary<string, TypeReference> dict = new Dictionary<string, TypeReference>();
587 for (int i = 0; i < realType.GenericParameters.Count; i++)
589 string p = realType.GenericParameters[i].Name;
590 TypeReference type = genericInstanceType.GenericArguments[i];
595 if (dict.ContainsKey(invoke.ReturnType.Name))
597 invoke.ReturnType = dict[invoke.ReturnType.Name];
600 for (int i = 0; i < invoke.Parameters.Count; i++)
602 if (dict.ContainsKey(invoke.Parameters[i].ParameterType.Name))
604 invoke.Parameters[i].ParameterType = dict[invoke.Parameters[i].ParameterType.Name];
610 if (!handler.ReturnType.InheritsFromOrImplements(invoke.ReturnType))
611 throw new XamlParseException($"Signature (return type) of EventHandler \"{context.Type.FullName}.{value}\" doesn't match the event type", iXmlLineInfo);
612 if (invoke.Parameters.Count != handler.Parameters.Count)
613 throw new XamlParseException($"Signature (number of arguments) of EventHandler \"{context.Type.FullName}.{value}\" doesn't match the event type", iXmlLineInfo);
614 if (!invoke.ContainsGenericParameter)
615 for (var i = 0; i < invoke.Parameters.Count; i++)
616 if (!handler.Parameters[i].ParameterType.InheritsFromOrImplements(invoke.Parameters[i].ParameterType))
617 throw new XamlParseException($"Signature (parameter {i}) of EventHandler \"{context.Type.FullName}.{value}\" doesn't match the event type", iXmlLineInfo);
619 new EXamlAddEvent(context, parent, context.Values[context.RootNode] as EXamlCreateObject, localName, handler);
622 static bool CanSetDynamicResource(MemberReference bpRef, INode valueNode, EXamlContext context)
626 var elementNode = valueNode as IElementNode;
627 if (elementNode == null)
630 var valueInstance = context.Values[valueNode] as EXamlCreateObject;
631 if (null == valueInstance)
634 return valueInstance.GetType().FullName == typeof(DynamicResource).FullName;
637 static void SetDynamicResource(EXamlCreateObject parent, MemberReference bpRef, IElementNode elementNode, IXmlLineInfo iXmlLineInfo, EXamlContext context)
639 var instance = context.Values[elementNode] as EXamlCreateObject;
640 if (null != instance)
642 var dynamicResource = instance.Instance as DynamicResource;
644 if (null != dynamicResource)
646 instance.IsValid = false;
647 new EXamlSetDynamicResource(context, parent, bpRef, dynamicResource.Key);
652 static bool CanSetBinding(MemberReference bpRef, INode valueNode, EXamlContext context)
654 var module = context.Module;
658 var elementNode = valueNode as IElementNode;
659 if (elementNode == null)
662 var valueInstance = context.Values[valueNode] as EXamlCreateObject;
663 if (null == valueInstance)
666 var implicitOperator = valueInstance.GetType().GetImplicitOperatorTo(module.ImportReference((XamlTask.bindingAssemblyName, XamlTask.bindingNameSpace, "BindingBase")), module);
667 if (implicitOperator != null)
670 return valueInstance.GetType().InheritsFromOrImplements(XamlTask.bindingNameSpace + ".BindingBase");
673 static void SetBinding(EXamlCreateObject parent, MemberReference bpRef, IElementNode elementNode, IXmlLineInfo iXmlLineInfo, EXamlContext context)
675 new EXamlSetBinding(context, parent, bpRef, context.Values[elementNode]);
678 static bool CanSetValue(MemberReference bpRef, bool attached, INode node, IXmlLineInfo iXmlLineInfo, EXamlContext context)
680 var module = context.Module;
685 var valueNode = node as ValueNode;
686 if (valueNode != null && valueNode.CanConvertValue(context.Module, bpRef))
689 var elementNode = node as IElementNode;
690 if (elementNode == null)
693 return context.Values.ContainsKey(elementNode);
695 //VariableDefinition varValue;
696 //if (!context.Variables.TryGetValue(elementNode, out varValue))
699 // var bpTypeRef = bpRef.GetBindablePropertyType(iXmlLineInfo, module);
700 //// If it's an attached BP, there's no second chance to handle IMarkupExtensions, so we try here.
701 //// Worst case scenario ? InvalidCastException at runtime
702 //if (attached && varValue.VariableType.FullName == "System.Object")
704 //var implicitOperator = varValue.VariableType.GetImplicitOperatorTo(bpTypeRef, module);
705 //if (implicitOperator != null)
708 ////as we're in the SetValue Scenario, we can accept value types, they'll be boxed
709 //if (varValue.VariableType.IsValueType && bpTypeRef.FullName == "System.Object")
712 //return varValue.VariableType.InheritsFromOrImplements(bpTypeRef);
715 static bool CanGetValue(EXamlCreateObject parent, MemberReference bpRef, bool attached, IXmlLineInfo iXmlLineInfo, EXamlContext context, out TypeReference propertyType)
717 var module = context.Module;
723 if (!parent.GetType().InheritsFromOrImplements(module.ImportReference((XamlTask.bindingAssemblyName, XamlTask.bindingNameSpace, "BindableObject"))))
726 propertyType = bpRef.GetBindablePropertyType(iXmlLineInfo, module);
730 static void SetValue(EXamlCreateObject parent, MemberReference bpRef, INode node, IXmlLineInfo iXmlLineInfo, EXamlContext context)
732 var valueNode = node as ValueNode;
734 if (valueNode != null)
736 var valueType = bpRef.GetBindablePropertyType(null, context.Module);
737 var converterType = valueNode.GetConverterType(new ICustomAttributeProvider[] { valueType.Resolve() });
738 if (null != converterType)
740 var converterValue = new EXamlValueConverterFromString(context, converterType.Resolve(), valueNode.Value as string);
741 context.Values[node] = new EXamlCreateObject(context, converterValue, valueType);
745 context.Values[node] = valueNode.GetBaseValue(context, valueType);
749 new EXamlSetBindalbeProperty(context, parent, bpRef, context.Values[node]);
752 static void GetValue(EXamlCreateObject parent, FieldReference bpRef, IXmlLineInfo iXmlLineInfo, EXamlContext context, out TypeReference propertyType)
754 var module = context.Module;
755 propertyType = bpRef.GetBindablePropertyType(iXmlLineInfo, module);
758 // Create(Ldloc, parent),
759 // Create(Ldsfld, bpRef),
760 // Create(Callvirt, module.ImportMethodReference((XamlCTask.bindingAssemblyName, XamlCTask.bindingNameSpace, "BindableObject"),
761 // methodName: "GetValue",
762 // parameterTypes: new[] { (XamlCTask.bindingAssemblyName, XamlCTask.bindingNameSpace, "BindableProperty") })),
766 static bool CanSet(EXamlCreateObject parent, string localName, INode node, EXamlContext context)
768 var module = context.Module;
769 TypeReference declaringTypeReference;
770 var property = parent.GetType().GetProperty(pd => pd.Name == localName, out declaringTypeReference);
771 if (property == null)
773 var propertyType = property.ResolveGenericPropertyType(declaringTypeReference, module);
774 var propertySetter = property.SetMethod;
775 if (propertySetter == null || !propertySetter.IsPublic || propertySetter.IsStatic)
778 var valueNode = node as ValueNode;
779 if (valueNode != null && valueNode.CanConvertValue(context.Module, propertyType, new ICustomAttributeProvider[] { property, propertyType.ResolveCached()}))
782 var elementNode = node as IElementNode;
783 if (elementNode == null)
786 var vardef = context.Variables[elementNode];
787 var implicitOperator = vardef.VariableType.GetImplicitOperatorTo(propertyType, module);
789 var value = context.Values[elementNode] as EXamlCreateObject;
791 if (vardef.VariableType.InheritsFromOrImplements(propertyType))
793 if (null != value && value.GetType().InheritsFromOrImplements(propertyType))
795 if (implicitOperator != null)
797 if (propertyType.FullName == "System.Object")
800 //I'd like to get rid of this condition. This comment used to be //TODO replace latest check by a runtime type check
801 if (vardef.VariableType.FullName == "System.Object")
804 var realValue = context.Values[elementNode] as EXamlCreateObject;
805 if (null != realValue)
807 var valueTypeRef = realValue.GetType();
808 if (valueTypeRef.InheritsFromOrImplements(propertyType))
813 var realTypeFromMarkupExtension = valueTypeRef.GetRealTypeIfIsMarkupExtension();
815 if (true == realTypeFromMarkupExtension?.InheritsFromOrImplements(propertyType))
824 static bool CanGet(EXamlCreateObject parent, string localName, EXamlContext context, out TypeReference propertyType)
826 var module = context.Module;
828 TypeReference declaringTypeReference;
829 var property = parent.GetType().GetProperty(pd => pd.Name == localName, out declaringTypeReference);
830 if (property == null)
832 var propertyGetter = property.GetMethod;
833 if (propertyGetter == null || !propertyGetter.IsPublic || propertyGetter.IsStatic)
836 module.ImportReference(parent.GetType().ResolveCached());
837 var propertyGetterRef = module.ImportReference(module.ImportReference(propertyGetter).ResolveGenericParameters(declaringTypeReference, module));
838 propertyGetterRef.ImportTypes(module);
839 propertyType = propertyGetterRef.ReturnType.ResolveGenericParameters(declaringTypeReference);
844 static void Set(EXamlCreateObject parent, string localName, INode node, IXmlLineInfo iXmlLineInfo, EXamlContext context)
846 var module = context.Module;
847 TypeReference declaringTypeReference;
848 var property = parent.Type.GetProperty(pd => pd.Name == localName, out declaringTypeReference);
849 var propertySetter = property.SetMethod;
851 //// IL_0007: ldloc.0
852 //// IL_0008: ldstr "foo"
853 //// IL_000d: callvirt instance void class [Tizen.NUI.Xaml.Core]Tizen.NUI.Xaml.Label::set_Text(string)
855 var propertySetterRef = module.ImportReference(module.ImportReference(propertySetter).ResolveGenericParameters(declaringTypeReference, module));
856 propertySetterRef.ImportTypes(module);
857 var propertyType = property.ResolveGenericPropertyType(declaringTypeReference, module);
858 var valueNode = node as ValueNode;
859 var elementNode = node as IElementNode;
861 //if it's a value type, load the address so we can invoke methods on it
862 if (parent.Type.IsValueType)
864 // yield return Instruction.Create(OpCodes.Ldloca, parent);
868 // yield return Instruction.Create(OpCodes.Ldloc, parent);
871 if (valueNode != null)
873 if ("Tizen.NUI.Binding.BindableProperty" == propertyType.FullName)
875 var bindableProperty = BindablePropertyConverter.GetBindablePropertyFieldReference(valueNode.Value as string, module, node as BaseNode);
876 var fieldRef = bindableProperty.DeclaringType.ResolveCached().Fields.FirstOrDefault(a => a.FullName == bindableProperty.FullName);
877 context.Values[node] = new EXamlCreateObject(context, bindableProperty.DeclaringType, fieldRef, null);
881 var converterType = valueNode.GetConverterType(new ICustomAttributeProvider[] { property, propertyType.ResolveCached() });
882 if (null != converterType)
884 var converterValue = new EXamlValueConverterFromString(context, converterType.Resolve(), valueNode.Value as string);
885 context.Values[node] = new EXamlCreateObject(context, converterValue, propertyType);
889 context.Values[node] = valueNode.GetBaseValue(context, property.PropertyType);
893 else if (elementNode != null)
895 var vardef = context.Variables[elementNode];
896 var implicitOperator = vardef.VariableType.GetImplicitOperatorTo(propertyType, module);
897 //yield return Instruction.Create(OpCodes.Ldloc, vardef);
898 if (!vardef.VariableType.InheritsFromOrImplements(propertyType) && implicitOperator != null)
900 // 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>)
901 //yield return Instruction.Create(OpCodes.Call, module.ImportReference(implicitOperator));
903 else if (!vardef.VariableType.IsValueType && propertyType.IsValueType)
905 //yield return Instruction.Create(OpCodes.Unbox_Any, module.ImportReference(propertyType));
907 else if (vardef.VariableType.IsValueType && propertyType.FullName == "System.Object")
909 //yield return Instruction.Create(OpCodes.Box, vardef.VariableType);
911 if (parent.Type.IsValueType)
913 //yield return Instruction.Create(OpCodes.Call, propertySetterRef);
917 //yield return Instruction.Create(OpCodes.Callvirt, propertySetterRef);
921 new EXamlSetProperty(context, parent, localName, context.Values[node]);
924 static void Get(EXamlCreateObject parent, string localName, IXmlLineInfo iXmlLineInfo, EXamlContext context, out TypeReference propertyType)
926 var module = context.Module;
927 var property = parent.GetType().GetProperty(pd => pd.Name == localName, out var declaringTypeReference);
928 var propertyGetter = property.GetMethod;
930 module.ImportReference(parent.GetType().ResolveCached());
931 var propertyGetterRef = module.ImportReference(module.ImportReference(propertyGetter).ResolveGenericParameters(declaringTypeReference, module));
932 propertyGetterRef.ImportTypes(module);
933 propertyType = propertyGetterRef.ReturnType.ResolveGenericParameters(declaringTypeReference);
935 //if (parent.VariableType.IsValueType)
937 // Instruction.Create(OpCodes.Ldloca, parent),
938 // Instruction.Create(OpCodes.Call, propertyGetterRef),
942 // Instruction.Create(OpCodes.Ldloc, parent),
943 // Instruction.Create(OpCodes.Callvirt, propertyGetterRef),
947 static bool CanAdd(EXamlCreateObject parent, XmlName propertyName, INode node, IXmlLineInfo lineInfo, EXamlContext context)
949 var module = context.Module;
950 var localName = propertyName.LocalName;
952 var bpRef = GetBindablePropertyReference(parent, propertyName.NamespaceURI, ref localName, out attached, context, lineInfo);
953 TypeReference propertyType;
955 if ( !CanGetValue(parent, bpRef, attached, null, context, out propertyType)
956 && !CanGet(parent, localName, context, out propertyType))
959 //TODO check md.Parameters[0] type
960 var adderTuple = propertyType.GetMethods(md => md.Name == "Add" && md.Parameters.Count == 1, module).FirstOrDefault();
961 if (adderTuple == null)
967 static Dictionary<EXamlCreateObject, IList<string>> resourceNamesInUse = new Dictionary<EXamlCreateObject, IList<string>>();
968 static bool CanAddToResourceDictionary(EXamlCreateObject parent, TypeReference collectionType, IElementNode node, IXmlLineInfo lineInfo, EXamlContext context)
970 if ( collectionType.FullName != "Tizen.NUI.Binding.ResourceDictionary"
971 && collectionType.ResolveCached().BaseType?.FullName != "Tizen.NUI.Binding.ResourceDictionary")
975 if (node.Properties.ContainsKey(XmlName.xKey)) {
976 var key = (node.Properties[XmlName.xKey] as ValueNode).Value as string;
977 if (!resourceNamesInUse.TryGetValue(parent, out var names))
978 resourceNamesInUse[parent] = (names = new List<string>());
979 if (names.Contains(key))
980 throw new XamlParseException($"A resource with the key '{key}' is already present in the ResourceDictionary.", lineInfo);
985 //is there a RD.Add() overrides that accepts this ?
986 var nodeTypeRef = context.Variables[node].VariableType;
987 var module = context.Module;
988 if (module.ImportMethodReference((XamlTask.bindingAssemblyName, XamlTask.bindingNameSpace, "ResourceDictionary"),
990 parameterTypes: new[] { (nodeTypeRef.Scope.Name, nodeTypeRef.Namespace, nodeTypeRef.Name) }) != null)
993 throw new XamlParseException("resources in ResourceDictionary require a x:Key attribute", lineInfo);
996 static void Add(EXamlCreateObject parent, string propertyName, INode node, IXmlLineInfo iXmlLineInfo, EXamlContext context)
999 var module = context.Module;
1000 var elementNode = node as IElementNode;
1002 TypeReference declaringTypeReference;
1003 var property = parent.Type.GetProperty(pd => pd.Name == propertyName, out declaringTypeReference);
1004 TypeReference propertyType = property.PropertyType;
1006 if (null != elementNode && CanAddToResourceDictionary(parent, propertyType, elementNode, iXmlLineInfo, context))
1008 var keyName = (elementNode.Properties[XmlName.xKey] as ValueNode).Value as string;
1009 new EXamlAddToResourceDictionary(context, parent, keyName, context.Values[node]);
1013 var adderTuple = propertyType.GetMethods(md => md.Name == "Add" && md.Parameters.Count == 1, module).FirstOrDefault();
1014 var adderRef = module.ImportReference(adderTuple.Item1);
1015 adderRef = module.ImportReference(adderRef.ResolveGenericParameters(adderTuple.Item2, module));
1016 var childType = GetParameterType(adderRef.Parameters[0]);
1018 if (node is ValueNode valueNode)
1020 if (true == valueNode.CanConvertValue(module, childType, (TypeReference)null))
1022 var obj = new EXamlGetObjectByProperty(context, parent, propertyName);
1024 var converterType = valueNode.GetConverterType(new ICustomAttributeProvider[] { property, childType.ResolveCached() });
1025 if (null != converterType)
1027 var converterValue = new EXamlValueConverterFromString(context, converterType.Resolve(), valueNode.Value as string);
1028 context.Values[node] = new EXamlCreateObject(context, converterValue, propertyType);
1032 context.Values[node] = valueNode.GetBaseValue(context, childType);
1034 new EXamlAddToCollectionProperty(context, obj, context.Values[node]);
1037 else if (node is ElementNode element)
1039 var obj = new EXamlGetObjectByProperty(context, parent, propertyName);
1040 new EXamlAddToCollectionProperty(context, obj, context.Values[node]);
1044 static IEnumerable<Instruction> AddToResourceDictionary(IElementNode node, IXmlLineInfo lineInfo, EXamlContext context)
1046 var module = context.Module;
1048 if (node.Properties.ContainsKey(XmlName.xKey)) {
1049 // IL_0014: ldstr "key"
1050 // IL_0019: ldstr "foo"
1051 // IL_001e: callvirt instance void class [Tizen.NUI.Xaml.Core]Tizen.NUI.Xaml.ResourceDictionary::Add(string, object)
1052 yield return Create(Ldstr, (node.Properties[XmlName.xKey] as ValueNode).Value as string);
1053 var varDef = context.Variables[node];
1054 yield return Create(Ldloc, varDef);
1055 if (varDef.VariableType.IsValueType)
1056 yield return Create(Box, module.ImportReference(varDef.VariableType));
1057 yield return Create(Callvirt, module.ImportMethodReference((XamlTask.bindingAssemblyName, XamlTask.bindingNameSpace, "ResourceDictionary"),
1059 parameterTypes: new[] {
1060 ("mscorlib", "System", "String"),
1061 ("mscorlib", "System", "Object"),
1066 var nodeTypeRef = context.Variables[node].VariableType;
1067 yield return Create(Ldloc, context.Variables[node]);
1068 yield return Create(Callvirt, module.ImportMethodReference((XamlTask.bindingAssemblyName, XamlTask.bindingNameSpace, "ResourceDictionary"),
1070 parameterTypes: new[] { (nodeTypeRef.Scope.Name, nodeTypeRef.Namespace, nodeTypeRef.Name) }));
1074 public static TypeReference GetParameterType(ParameterDefinition param)
1076 if (!param.ParameterType.IsGenericParameter)
1077 return param.ParameterType;
1078 var type = (param.Method as MethodReference).DeclaringType as GenericInstanceType;
1079 return type.GenericArguments [0];
1082 static bool GetNameAndTypeRef(ref TypeReference elementType, string namespaceURI, ref string localname,
1083 EXamlContext context, IXmlLineInfo lineInfo)
1085 var dotIdx = localname.IndexOf('.');
1088 var typename = localname.Substring(0, dotIdx);
1089 localname = localname.Substring(dotIdx + 1);
1090 elementType = new XmlType(namespaceURI, typename, null).GetTypeReference(context.Module, lineInfo);
1096 static void SetDataTemplate(ElementNode parentNode, ElementNode rootnode, EXamlContext parentContext,
1097 IXmlLineInfo xmlLineInfo)
1099 var typeref = parentContext.Module.ImportReference(rootnode.XmlType.GetTypeReference(parentContext.Module, rootnode));
1100 var visitorContext = new EXamlContext(typeref.ResolveCached(), typeref.Module);
1102 rootnode.Accept(new XamlNodeVisitor((node, parent) => node.Parent = parent), null);
1103 rootnode.Accept(new EXamlExpandMarkupsVisitor(visitorContext), null);
1104 rootnode.Accept(new PruneIgnoredNodesVisitor(), null);
1105 rootnode.Accept(new EXamlCreateObjectVisitor(visitorContext), null);
1106 rootnode.Accept(new EXamlSetNamescopesAndRegisterNamesVisitor(visitorContext), null);
1107 rootnode.Accept(new EXamlSetFieldVisitor(visitorContext), null);
1108 rootnode.Accept(new EXamlSetResourcesVisitor(visitorContext), null);
1109 rootnode.Accept(new EXamlSetPropertiesVisitor(visitorContext, true), null);
1111 var eXamlString = visitorContext.GenerateEXamlString();
1113 var parentTyperef = parentContext.Module.ImportReference(parentNode.XmlType.GetTypeReference(parentContext.Module, parentNode));
1115 if (parentContext.Values[parentNode] is EXamlCreateObject eXamlObject)
1117 eXamlObject.IsValid = false;
1118 parentContext.Values[parentNode] = new EXamlCreateDataTemplate(parentContext, parentTyperef, eXamlString);
1122 bool TrySetRuntimeName(XmlName propertyName, EXamlCreateObject variableDefinition, ValueNode node)
1124 if (null == variableDefinition)
1129 if (propertyName != XmlName.xName)
1132 var attributes = variableDefinition.GetType().ResolveCached()
1133 .CustomAttributes.Where(attribute => attribute.AttributeType.FullName == "Tizen.NUI.Xaml.RuntimeNamePropertyAttribute").ToList();
1135 if (!attributes.Any())
1138 var runTimeName = attributes[0].ConstructorArguments[0].Value as string;
1140 if (string.IsNullOrEmpty(runTimeName))
1143 SetPropertyValue(variableDefinition, new XmlName("", runTimeName), node, Context, node);