From: jpbruyere Date: Wed, 14 Dec 2016 00:03:55 +0000 (+0100) Subject: Template binding instantiation, dynamic event handler emition. X-Git-Tag: v0.5.1~63^2~32 X-Git-Url: https://git.osiis.dedyn.io/?a=commitdiff_plain;h=bdd6f9a1db93cbe52ad35e17d909fd8eb28368d5;p=jp%2Fcrow.git Template binding instantiation, dynamic event handler emition. intermediate save. modifié : src/CompilerServices/CompilerServices.cs modifié : src/IML/Context.cs modifié : src/IML/Node.cs modifié : src/IML/NodeAddress.cs modifié : src/Instantiator.cs --- diff --git a/src/CompilerServices/CompilerServices.cs b/src/CompilerServices/CompilerServices.cs index 85e240e9..8f587d39 100644 --- a/src/CompilerServices/CompilerServices.cs +++ b/src/CompilerServices/CompilerServices.cs @@ -16,7 +16,7 @@ namespace Crow BindingFlags.NonPublic | BindingFlags.Instance); internal static MethodInfo stringEquals = typeof (string).GetMethod ("Equals", new Type [3] { typeof (string), typeof (string), typeof (StringComparison) }); - static MethodInfo miFindByName = typeof (GraphicObject).GetMethod ("FindByName"); + public static MethodInfo miFindByName = typeof (GraphicObject).GetMethod ("FindByName"); public static MethodInfo miIFaceLoad = typeof(Interface).GetMethod ("Load", BindingFlags.Instance | BindingFlags.Public); public static MethodInfo miGetITemp = typeof(Interface).GetMethod ("GetItemTemplate"); diff --git a/src/IML/Context.cs b/src/IML/Context.cs index 965d0062..4f9ba30d 100644 --- a/src/IML/Context.cs +++ b/src/IML/Context.cs @@ -38,7 +38,6 @@ namespace Crow.IML { public XmlTextReader reader = null; public Type RootType = null; - public int CurrentIndex = 0; public DynamicMethod dm = null; public ILGenerator il = null; @@ -46,7 +45,6 @@ namespace Crow.IML public NodeStack nodesStack = new NodeStack (); public Dictionary> Names = new Dictionary>(); - public Dictionary> PropertyBindings = new Dictionary> (); public Dictionary>> Bindings = new Dictionary>>(); @@ -75,21 +73,6 @@ namespace Crow.IML get { return nodesStack.Peek().CrowType; } } - public MethodInfo CurrentAddMethod { - get { - if (typeof (Group).IsAssignableFrom (CurrentNodeType)) - return CompilerServices.miAddChild; - if (typeof (Container).IsAssignableFrom (CurrentNodeType)) - return CompilerServices.miSetChild; - if (typeof (TemplatedContainer).IsAssignableFrom (CurrentNodeType)) - return CurrentIndex < 0 ? CompilerServices.miLoadTmp : CompilerServices.miSetContent; - if (typeof (TemplatedGroup).IsAssignableFrom (CurrentNodeType)) - return CurrentIndex < 0 ? CompilerServices.miLoadTmp : CompilerServices.miAddItem; - if (typeof (TemplatedControl).IsAssignableFrom (CurrentNodeType)) - return CompilerServices.miLoadTmp; - return null; - } - } void initILGen () { il.DeclareLocal (typeof (GraphicObject)); diff --git a/src/IML/Node.cs b/src/IML/Node.cs index 14cffdb2..a9e9bb80 100644 --- a/src/IML/Node.cs +++ b/src/IML/Node.cs @@ -26,14 +26,13 @@ using System.Collections.Generic; namespace Crow.IML { /// - /// IML Node are the elements of the interface XML, + /// IML Node are defined with a type and the index in the parent, /// public struct Node { + /// Current node type public Type CrowType; - /// - /// Indexer for group child, -1 for template - /// + /// Index in parent, -1 for template public int Index; public Node (Type crowType, int _index = 0) @@ -42,6 +41,20 @@ namespace Crow.IML Index = _index; } + public MethodInfo GetAddMethod(int childIdx){ + if (typeof (Group).IsAssignableFrom (CrowType)) + return CompilerServices.miAddChild; + if (typeof (Container).IsAssignableFrom (CrowType)) + return CompilerServices.miSetChild; + if (typeof (TemplatedContainer).IsAssignableFrom (CrowType)) + return childIdx < 0 ? CompilerServices.miLoadTmp : CompilerServices.miSetContent; + if (typeof (TemplatedGroup).IsAssignableFrom (CrowType)) + return childIdx < 0 ? CompilerServices.miLoadTmp : CompilerServices.miAddItem; + if (typeof (TemplatedControl).IsAssignableFrom (CrowType)) + return CompilerServices.miLoadTmp; + return null; + } + #region Equality Compare public override bool Equals (object obj) { diff --git a/src/IML/NodeAddress.cs b/src/IML/NodeAddress.cs index 1c907ec1..460998da 100644 --- a/src/IML/NodeAddress.cs +++ b/src/IML/NodeAddress.cs @@ -31,7 +31,7 @@ namespace Crow.IML public NodeAddress (Node[] nodes) : base(nodes) { } - public Type NodeType { get { return this[this.Count -1].CrowType; }} + public Type NodeType { get { return Count == 0 ? null : this[this.Count -1].CrowType; }} public override bool Equals (object obj) { diff --git a/src/Instantiator.cs b/src/Instantiator.cs index b5ffbe83..7804cd15 100644 --- a/src/Instantiator.cs +++ b/src/Instantiator.cs @@ -85,8 +85,11 @@ namespace Crow List dsValueChangedDynMeths = new List(); List dataSourceChangedDelegates = new List(); + List templateValueChangedDelegates = new List(); Dictionary bindingDelegates = new Dictionary();//valuechanged del Dictionary bindingInitializer = new Dictionary();//initialize with actual values of binding origine + List eventDynHandlers = new List(); + Delegate templateBinding; #region IML parsing /// @@ -95,7 +98,9 @@ namespace Crow void parseIML (XmlTextReader reader) { Context ctx = new Context (findRootType (reader)); - emitLoader (reader, ctx, ctx.RootType); + ctx.nodesStack.Push (new Node (ctx.RootType)); + emitLoader (reader, ctx); + ctx.nodesStack.Pop (); emitBindingDelegates (ctx); @@ -129,9 +134,8 @@ namespace Crow } return t; } - void emitLoader (XmlTextReader reader, Context ctx, Type newType) + void emitLoader (XmlTextReader reader, Context ctx) { - ctx.nodesStack.Push(new Node (newType, ctx.CurrentIndex)); string tmpXml = reader.ReadOuterXml (); if (ctx.nodesStack.Peek().HasTemplate) @@ -140,10 +144,8 @@ namespace Crow emitGOLoad (ctx, tmpXml); emitCheckAndBindValueChanged (ctx); - - ctx.nodesStack.Pop (); } - void emitTemplateLoad (Context ctx, string tmpXml) { + void emitTemplateLoad (Context ctx, string tmpXml) { //if its a template, first read template elements using (XmlTextReader reader = new XmlTextReader (tmpXml, XmlNodeType.Element, null)) { List itemTemplateIds = new List (); @@ -159,8 +161,7 @@ namespace Crow if (reader.Name == "Template") { inlineTemplate = true; reader.Read (); - ctx.CurrentIndex = -1; - readChildren (reader, ctx); + readChildren (reader, ctx, -1); } else if (reader.Name == "ItemTemplate") { string dataType = "default", datas = "", path = ""; while (reader.MoveToNextAttribute ()) { @@ -251,7 +252,9 @@ namespace Crow if (mi == null) throw new Exception ("Member '" + reader.Name + "' not found in " + ctx.CurrentNodeType.Name); if (mi.MemberType == MemberTypes.Event) { - //CompilerServices.emitBindingCreation (ctx.il, reader.Name, reader.Value); + if (reader.Value.StartsWith ("{", StringComparison.OrdinalIgnoreCase)) + emitEventHandler(ctx, mi as EventInfo,reader.Value.Substring (1, reader.Value.Length - 2)); + //else //emit handler binding continue; } PropertyInfo pi = mi as PropertyInfo; @@ -260,10 +263,10 @@ namespace Crow if (pi.Name == "Name"){ if (!ctx.Names.ContainsKey(reader.Value)) - ctx.Names[reader.Value] = new List(); + ctx.Names[reader.Value] = new List(); ctx.Names[reader.Value].Add(ctx.CurrentNodeAddress); } - + if (reader.Value.StartsWith ("{", StringComparison.OrdinalIgnoreCase)) readPropertyBinding (ctx, reader.Name, reader.Value.Substring (1, reader.Value.Length - 2)); else @@ -274,18 +277,18 @@ namespace Crow } #endregion - if (!reader.IsEmptyElement) { - ctx.CurrentIndex = 0; - readChildren (reader, ctx); - } + readChildren (reader, ctx); + + ctx.nodesStack.ResetCurrentNodeIndex (); } } /// /// Parse child node an generate corresponding msil /// - void readChildren (XmlTextReader reader, Context ctx) + void readChildren (XmlTextReader reader, Context ctx, int startingIdx = 0) { bool endTagReached = false; + int nodeIdx = startingIdx; while (reader.Read ()) { switch (reader.NodeType) { case XmlNodeType.EndElement: @@ -319,13 +322,15 @@ namespace Crow ctx.il.Emit (OpCodes.Stloc_0);//child is now loc_0 CompilerServices.emitSetCurInterface (ctx.il); - emitLoader (reader, ctx, t); + ctx.nodesStack.Push (new Node (t, nodeIdx)); + emitLoader (reader, ctx); + ctx.nodesStack.Pop (); ctx.il.Emit (OpCodes.Ldloc_0);//load child on stack for parenting - ctx.il.Emit (OpCodes.Callvirt, ctx.CurrentAddMethod); + ctx.il.Emit (OpCodes.Callvirt, ctx.nodesStack.Peek().GetAddMethod(nodeIdx)); ctx.il.Emit (OpCodes.Stloc_0); //reset local to current go - ctx.CurrentIndex++; + nodeIdx++; break; } @@ -335,7 +340,6 @@ namespace Crow } #endregion - void emitCheckAndBindValueChanged(Context ctx){ System.Reflection.Emit.Label labContinue = ctx.il.DefineLabel (); string strNA = ctx.CurrentNodeAddress.ToString (); @@ -357,7 +361,7 @@ namespace Crow //attach to valuechanged handler ctx.il.Emit(OpCodes.Callvirt, typeof(IValueChange).GetEvent("ValueChanged").AddMethod); - //call initializer + //call initializer ctx.il.Emit(OpCodes.Ldarg_0);//load ref to this instanciator onto the stack ctx.il.Emit(OpCodes.Ldfld, typeof(Instantiator).GetField("bindingInitializer", BindingFlags.Instance | BindingFlags.NonPublic)); ctx.il.Emit (OpCodes.Ldstr, strNA);//load binding id for current node @@ -367,11 +371,7 @@ namespace Crow ctx.il.MarkLabel (labContinue); } - /// - /// Tries the find target. - /// - /// The member address in the graphic tree, null if on dataSource, - /// Expression. + void readPropertyBinding (Context ctx, string sourceMember, string expression) { MemberAddress targetMember; @@ -382,7 +382,7 @@ namespace Crow //if binding exp = '{}' => binding is done on datasource if (string.IsNullOrEmpty (expression)) - return; + return; if (expression.StartsWith ("²")) { expression = expression.Substring (1); @@ -398,26 +398,8 @@ namespace Crow } NodeAddress currentNode = ctx.CurrentNodeAddress; - int ptr = currentNode.Count - 1; - //if exp start with '/' => Graphic tree parsing start at source - if (string.IsNullOrEmpty (bindingExp [0])) { - //TODO: - } else if (bindingExp [0] == ".") { //search template root - while (ptr > 0) { - ptr--; - if (typeof(TemplatedControl).IsAssignableFrom (currentNode [ptr].CrowType)) - break; - } - } else if (bindingExp [0] == "..") { //search starting at current node - int levelUp = bindingExp.Length - 1; - if (levelUp > ptr) - throw new Exception ("Binding error: try to bind outside IML source"); - ptr -= levelUp; - } - Node[] targetNode = new Node[ptr+1]; - Array.Copy (currentNode.ToArray (), targetNode, ptr + 1); - NodeAddress targetNA = new NodeAddress (targetNode); + NodeAddress targetNA = getNodeAdressFromBindingExp (currentNode, bindingExp); string [] bindTrg = bindingExp.Last().Split ('.'); @@ -468,6 +450,30 @@ namespace Crow Debug.WriteLine ("Property less binding: " + Target + expression); #endif } + NodeAddress getNodeAdressFromBindingExp(NodeAddress currentNode, string[] bindingExp){ + int ptr = currentNode.Count - 1; + + //if exp start with '/' => Graphic tree parsing start at source + if (string.IsNullOrEmpty (bindingExp [0])) { + //TODO: + } else if (bindingExp [0] == ".") { //search template root + ptr--; + while (ptr >= 0) { + if (typeof(TemplatedControl).IsAssignableFrom (currentNode [ptr].CrowType)) + break; + ptr--; + } + + } else if (bindingExp [0] == "..") { //search starting at current node + int levelUp = bindingExp.Length - 1; + if (levelUp > ptr) + throw new Exception ("Binding error: try to bind outside IML source"); + ptr -= levelUp; + } + Node[] targetNode = new Node[ptr+1]; + Array.Copy (currentNode.ToArray (), targetNode, ptr + 1); + return new NodeAddress (targetNode); + } /// /// create the valuechanged handler, the datasourcechanged handler and emit event handling /// @@ -531,8 +537,8 @@ namespace Crow #endregion #region emit dataSourceChanged event handler - //now we create the datasource changed method that will init the destination member with - //the actual value of the origin member of the datasource and then will bind the value changed + //now we create the datasource changed method that will init the destination member with + //the actual value of the origin member of the datasource and then will bind the value changed //dyn methode. //dm is bound to the instanciator instance to have access to cached dyn meth and delegates dm = new DynamicMethod ("dyn_dschanged", @@ -569,8 +575,7 @@ namespace Crow //store dschange delegate in instatiator instance for access while instancing graphic object int delDSIndex = dataSourceChangedDelegates.Count; - Delegate del = dm.CreateDelegate (CompilerServices.ehTypeDSChange, this); - dataSourceChangedDelegates.Add(del); + dataSourceChangedDelegates.Add(dm.CreateDelegate (CompilerServices.ehTypeDSChange, this)); #endregion #region Emit datasourcechanged handler binding in the loader context @@ -578,10 +583,150 @@ namespace Crow ctx.il.Emit(OpCodes.Ldarg_0);//load ref to this instanciator onto the stack ctx.il.Emit(OpCodes.Ldfld, typeof(Instantiator).GetField("dataSourceChangedDelegates", BindingFlags.Instance | BindingFlags.NonPublic)); ctx.il.Emit(OpCodes.Ldc_I4, delDSIndex);//load delegate index - ctx.il.Emit(OpCodes.Callvirt, typeof(List).GetMethod("get_Item", new Type[] { typeof(Int32) })); + ctx.il.Emit(OpCodes.Callvirt, typeof(List).GetMethod("get_Item", new Type[] { typeof(Int32) })); ctx.il.Emit(OpCodes.Callvirt, typeof(GraphicObject).GetEvent("DataSourceChanged").AddMethod);//call add event #endregion } + /// + /// Compile events expression in IML attributes, and store the result in the instanciator + /// Those handlers will be bound when instatiing + /// + void emitEventHandler (Context ctx, EventInfo sourceEvent, string expression) + { + #if DEBUG_BINDING + Debug.WriteLine ("\tCompile Event Source "); + #endif + + + #region Retrieve EventHandler parameter type + MethodInfo evtInvoke = sourceEvent.EventHandlerType.GetMethod ("Invoke"); + ParameterInfo [] evtParams = evtInvoke.GetParameters (); + Type handlerArgsType = evtParams [1].ParameterType; + #endregion + + Type [] args = { typeof (object), handlerArgsType }; + DynamicMethod dm = new DynamicMethod ("dyn_eventHandler", + typeof(void), + args, true); + + + #region IL generation + NodeAddress currentNode = ctx.CurrentNodeAddress; + string strNA = currentNode.ToString(); + + ILGenerator il = dm.GetILGenerator (256); + il.Emit (OpCodes.Nop); + + string [] srcLines = expression.Trim ().Split (new char [] { ';' }); + + foreach (string srcLine in srcLines) { + string statement = srcLine.Trim (); + + string [] operandes = statement.Split (new char [] { '=' }); + if (operandes.Length < 2) //not an affectation + { + //maybe we could handle here handler function name + continue; + } + + string rop = operandes [operandes.Length - 1].Trim (); + + #region LEFT OPERANDES + string [] lopParts = operandes [0].Trim ().Split ('/'); + Type lopType = currentNode.NodeType; + MemberInfo lopMI = null; + + il.Emit (OpCodes.Ldarg_0); //load sender ref onto the stack + + if (lopParts.Length > 1) { + NodeAddress lopNA = getNodeAdressFromBindingExp (currentNode, lopParts); + emitGetInstance (il, currentNode, lopNA); + lopType = lopNA.NodeType; + } + + string [] bindTrg = lopParts.Last().Split ('.'); + + if (bindTrg.Length == 1) + lopMI = lopType.GetMember (bindTrg [0]).FirstOrDefault(); + else if (bindTrg.Length == 2) { + //named target + //TODO: + il.Emit(OpCodes.Ldstr, bindTrg[0]); + il.Emit(OpCodes.Callvirt, typeof(GraphicObject).GetMethod("FindByName")); + lopMI = lopType.GetMember (bindTrg [1]).FirstOrDefault(); + } else + throw new Exception ("Syntax error in binding, expected 'go dot member'"); + + + if (lopMI == null) + throw new Exception (string.Format ("IML BINDING: Member not found")); + + OpCode lopSetOpCode; + dynamic lopSetMI; + Type lopT = null; + switch (lopMI.MemberType) { + case MemberTypes.Property: + lopSetOpCode = OpCodes.Callvirt; + PropertyInfo lopPi = lopMI as PropertyInfo; + lopT = lopPi.PropertyType; + lopSetMI = lopPi.GetSetMethod (); + break; + case MemberTypes.Field: + lopSetOpCode = OpCodes.Stfld; + FieldInfo dstFi = lopMI as FieldInfo; + lopT = dstFi.FieldType; + lopSetMI = dstFi; + break; + default: + throw new Exception (string.Format ("GOML:member type not handle")); + } + #endregion + + #region RIGHT OPERANDES + if (rop.StartsWith ("\'")) { + if (!rop.EndsWith ("\'")) + throw new Exception (string.Format + ("GOML:malformed string constant in handler: {0}", rop)); + string strcst = rop.Substring (1, rop.Length - 2); + + il.Emit (OpCodes.Ldstr, strcst); + + } else { + if (lopT.IsEnum) + throw new NotImplementedException (); + + MethodInfo lopParseMi = lopT.GetMethod ("Parse"); + if (lopParseMi == null) + throw new Exception (string.Format + ("GOML:no parse method found in: {0}", lopT.Name)); + il.Emit (OpCodes.Ldstr, rop); + il.Emit (OpCodes.Callvirt, lopParseMi); + il.Emit (OpCodes.Unbox_Any, lopT); + } + + #endregion + + //emit left operand assignment + il.Emit (lopSetOpCode, lopSetMI); + } + + il.Emit (OpCodes.Ret); + + #endregion + + //store event handler dynamic method in instanciator + int dmIdx = eventDynHandlers.Count; + eventDynHandlers.Add (dm.CreateDelegate (sourceEvent.EventHandlerType)); + + #region Emit event handler binding in the loader context + ctx.il.Emit(OpCodes.Ldloc_0);//load ref to current graphic object + ctx.il.Emit(OpCodes.Ldarg_0);//load ref to this instanciator onto the stack + ctx.il.Emit(OpCodes.Ldfld, typeof(Instantiator).GetField("eventDynHandlers", BindingFlags.Instance | BindingFlags.NonPublic)); + ctx.il.Emit(OpCodes.Ldc_I4, dmIdx);//load delegate index + ctx.il.Emit(OpCodes.Callvirt, typeof(List).GetMethod("get_Item", new Type[] { typeof(Int32) })); + ctx.il.Emit(OpCodes.Callvirt, sourceEvent.AddMethod);//call add event + #endregion + } /// /// Create and store in the instanciator the ValueChanged delegates @@ -589,129 +734,240 @@ namespace Crow /// need to be bound to destination instance as in the ancient system. /// void emitBindingDelegates(Context ctx){ - foreach (KeyValuePair>> bindings in ctx.Bindings ) { + foreach (KeyValuePair>> bindings in ctx.Bindings ) { + if (bindings.Key.Count == 0) + emitTemplateBindingDelegate (ctx, bindings.Value); + else + emitBindingDelegate (bindings.Key, bindings.Value); + } + } + void emitTemplateBindingDelegate(Context ctx, Dictionary> bindings){ + //value changed dyn method + DynamicMethod dm = new DynamicMethod ("dyn_tmpValueChanged", + typeof (void), CompilerServices.argsValueChange, true); + + ILGenerator il = dm.GetILGenerator (256); + + System.Reflection.Emit.Label endMethod = il.DefineLabel (); - Type origineNodeType = bindings.Key.NodeType; + il.DeclareLocal (typeof(object)); - //init method, current instance passed as arg - DynamicMethod dmInit = new DynamicMethod ("dyn_initBinding", - typeof (void), new Type[] {typeof(object)}, true); - ILGenerator ilInit = dmInit.GetILGenerator (256); - ilInit.Emit (OpCodes.Nop); + il.Emit (OpCodes.Nop); - //value changed dyn method - DynamicMethod dm = new DynamicMethod ("dyn_valueChanged", - typeof (void), CompilerServices.argsValueChange, true); + int i = 0; + foreach (KeyValuePair> bindingCase in bindings ) { - ILGenerator il = dm.GetILGenerator (256); + System.Reflection.Emit.Label nextTest = il.DefineLabel (); - System.Reflection.Emit.Label endMethod = il.DefineLabel (); + #region member name test + //load source member name + il.Emit (OpCodes.Ldarg_1); + il.Emit (OpCodes.Ldfld, typeof(ValueChangeEventArgs).GetField ("MemberName")); - il.DeclareLocal (typeof(object)); + il.Emit (OpCodes.Ldstr, bindingCase.Key);//load name to test + il.Emit (OpCodes.Ldc_I4_4);//StringComparison.Ordinal + il.Emit (OpCodes.Callvirt, CompilerServices.stringEquals); + il.Emit (OpCodes.Brfalse, nextTest);//if not equal, jump to next case + #endregion - il.Emit (OpCodes.Nop); - //il.Emit (OpCodes.Ldarg_0);//load source instance of ValueChanged event + #region destination member affectations - int i = 0; - foreach (KeyValuePair> bindingCase in bindings.Value ) { - Type origineType = origineNodeType.GetProperty (bindingCase.Key).PropertyType; + foreach (MemberAddress ma in bindingCase.Value) { + //first we have to load destination instance onto the stack, it is access + //with graphic tree functions deducted from nodes topology + il.Emit (OpCodes.Ldarg_0);//load source instance of ValueChanged event - System.Reflection.Emit.Label nextTest = il.DefineLabel (); + emitGetChild (il, typeof(TemplatedControl), -1); + emitGetInstance (il, ma.Address); - #region member name test - //load source member name + //load new value il.Emit (OpCodes.Ldarg_1); - il.Emit (OpCodes.Ldfld, typeof(ValueChangeEventArgs).GetField ("MemberName")); + il.Emit (OpCodes.Ldfld, typeof (ValueChangeEventArgs).GetField ("NewValue")); + + Type dstType = ma.Property.PropertyType; + if (dstType == typeof (string)) + il.Emit (OpCodes.Callvirt, CompilerServices.miObjToString); + else if (dstType.IsValueType) { + il.Emit (OpCodes.Unbox_Any, dstType);//TODO:double check this + }else + il.Emit (OpCodes.Castclass, dstType); + + il.Emit (OpCodes.Callvirt, ma.Property.GetSetMethod()); + } + #endregion + il.Emit (OpCodes.Br, endMethod); + il.MarkLabel (nextTest); + + i++; + } + //il.Emit (OpCodes.Pop); + il.MarkLabel (endMethod); + il.Emit (OpCodes.Ret); + + //store template bindings in instanciator + templateBinding = dm.CreateDelegate (typeof(EventHandler)); - il.Emit (OpCodes.Ldstr, bindingCase.Key);//load name to test - il.Emit (OpCodes.Ldc_I4_4);//StringComparison.Ordinal - il.Emit (OpCodes.Callvirt, CompilerServices.stringEquals); - il.Emit (OpCodes.Brfalse, nextTest);//if not equal, jump to next case - #endregion + #region emit ParentChanged handler + dm = new DynamicMethod ("dyn_parentChanged", + typeof (void), + CompilerServices.argsDSChange, true); - #region destination member affectations - foreach (MemberAddress ma in bindingCase.Value) { - //for initialisation dynmeth, load current instance - ilInit.Emit(OpCodes.Ldarg_0); - //first we have to load destination instance onto the stack, it is access - //with graphic tree functions deducted from nodes topology - il.Emit (OpCodes.Ldarg_0);//load source instance of ValueChanged event + il = dm.GetILGenerator (256); - NodeAddress origine = bindings.Key; - NodeAddress destination = ma.Address; + il.Emit (OpCodes.Nop); - emitGetInstance(il,origine,destination); - emitGetInstance(ilInit,origine,destination); + //load new parent onto the stack for handler addition + il.Emit (OpCodes.Ldarg_2); + il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); + + //Load cached delegate + il.Emit(OpCodes.Ldarg_0);//load ref to this instanciator onto the stack + il.Emit(OpCodes.Ldfld, typeof(Instantiator).GetField("templateBinding", BindingFlags.Instance | BindingFlags.NonPublic)); + + //add template bindings dynValueChanged delegate to new parent event + il.Emit(OpCodes.Callvirt, typeof(IValueChange).GetEvent("ValueChanged").AddMethod);//call add event + il.Emit (OpCodes.Ret); - //init dynmeth: load actual value - ilInit.Emit (OpCodes.Ldarg_0); - ilInit.Emit (OpCodes.Callvirt, origineNodeType.GetProperty (bindingCase.Key).GetGetMethod()); + //store dschange delegate in instatiator instance for access while instancing graphic object + int delDSIndex = dataSourceChangedDelegates.Count; + dataSourceChangedDelegates.Add(dm.CreateDelegate (CompilerServices.ehTypeDSChange, this)); + #endregion - //load new value - il.Emit (OpCodes.Ldarg_1); - il.Emit (OpCodes.Ldfld, typeof (ValueChangeEventArgs).GetField ("NewValue")); + #region Emit datasourcechanged handler binding in the loader context + ctx.il.Emit(OpCodes.Ldloc_0);//load ref to current graphic object + ctx.il.Emit(OpCodes.Ldarg_0);//load ref to this instanciator onto the stack + ctx.il.Emit(OpCodes.Ldfld, typeof(Instantiator).GetField("dataSourceChangedDelegates", BindingFlags.Instance | BindingFlags.NonPublic)); + ctx.il.Emit(OpCodes.Ldc_I4, delDSIndex);//load delegate index + ctx.il.Emit(OpCodes.Callvirt, typeof(List).GetMethod("get_Item", new Type[] { typeof(Int32) })); + ctx.il.Emit(OpCodes.Callvirt, typeof(GraphicObject).GetEvent("ParentChanged").AddMethod);//call add event + #endregion - emitConvert(ilInit,origineType,ma.Property.PropertyType); - emitConvert(il,origineType, ma.Property.PropertyType); + } + void emitBindingDelegate(NodeAddress origine, Dictionary> bindings){ + Type origineNodeType = origine.NodeType; - //set value - ilInit.Emit (OpCodes.Callvirt, ma.Property.GetSetMethod()); - il.Emit (OpCodes.Callvirt, ma.Property.GetSetMethod()); + //init method, current instance passed as arg + DynamicMethod dmInit = new DynamicMethod ("dyn_initBinding", + typeof (void), new Type[] {typeof(object)}, true); + ILGenerator ilInit = dmInit.GetILGenerator (256); + ilInit.Emit (OpCodes.Nop); - il.Emit (OpCodes.Br, endMethod); - il.MarkLabel (nextTest); - } - #endregion + //value changed dyn method + DynamicMethod dm = new DynamicMethod ("dyn_valueChanged", + typeof (void), CompilerServices.argsValueChange, true); + + ILGenerator il = dm.GetILGenerator (256); + + System.Reflection.Emit.Label endMethod = il.DefineLabel (); + + il.DeclareLocal (typeof(object)); + + il.Emit (OpCodes.Nop); + + int i = 0; + foreach (KeyValuePair> bindingCase in bindings ) { + + System.Reflection.Emit.Label nextTest = il.DefineLabel (); + + #region member name test + //load source member name + il.Emit (OpCodes.Ldarg_1); + il.Emit (OpCodes.Ldfld, typeof(ValueChangeEventArgs).GetField ("MemberName")); + + il.Emit (OpCodes.Ldstr, bindingCase.Key);//load name to test + il.Emit (OpCodes.Ldc_I4_4);//StringComparison.Ordinal + il.Emit (OpCodes.Callvirt, CompilerServices.stringEquals); + il.Emit (OpCodes.Brfalse, nextTest);//if not equal, jump to next case + #endregion + + #region destination member affectations + Type origineType = origineNodeType.GetProperty (bindingCase.Key).PropertyType; + foreach (MemberAddress ma in bindingCase.Value) { + //for initialisation dynmeth, load current instance + ilInit.Emit(OpCodes.Ldarg_0); + //first we have to load destination instance onto the stack, it is access + //with graphic tree functions deducted from nodes topology + il.Emit (OpCodes.Ldarg_0);//load source instance of ValueChanged event + + NodeAddress destination = ma.Address; + + emitGetInstance (il, origine, destination); + emitGetInstance (ilInit, origine, destination); + + //init dynmeth: load actual value + ilInit.Emit (OpCodes.Ldarg_0); + ilInit.Emit (OpCodes.Callvirt, origineNodeType.GetProperty (bindingCase.Key).GetGetMethod()); + + //load new value + il.Emit (OpCodes.Ldarg_1); + il.Emit (OpCodes.Ldfld, typeof (ValueChangeEventArgs).GetField ("NewValue")); + + if (origineType != ma.Property.PropertyType)//no unboxing required + emitConvert (ilInit, origineType, ma.Property.PropertyType); + emitConvert (il, origineType, ma.Property.PropertyType);//unboxing required + + //set value + ilInit.Emit (OpCodes.Callvirt, ma.Property.GetSetMethod()); + il.Emit (OpCodes.Callvirt, ma.Property.GetSetMethod()); - i++; } - //il.Emit (OpCodes.Pop); - il.MarkLabel (endMethod); - ilInit.Emit (OpCodes.Ret); - il.Emit (OpCodes.Ret); + #endregion + il.Emit (OpCodes.Br, endMethod); + il.MarkLabel (nextTest); - bindingDelegates [bindings.Key.ToString()] = dm.CreateDelegate (typeof(EventHandler)); - bindingInitializer [bindings.Key.ToString()] = dmInit.CreateDelegate (typeof(Action)); + i++; } + //il.Emit (OpCodes.Pop); + il.MarkLabel (endMethod); + ilInit.Emit (OpCodes.Ret); + il.Emit (OpCodes.Ret); + + bindingDelegates [origine.ToString()] = dm.CreateDelegate (typeof(EventHandler)); + bindingInitializer [origine.ToString()] = dmInit.CreateDelegate (typeof(Action)); } void emitGetInstance (ILGenerator il, NodeAddress orig, NodeAddress dest){ - if (orig.Count < dest.Count){ - for (int i = orig.Count-1; i < dest.Count-1; i++){ - if (typeof (Group).IsAssignableFrom (dest[i].CrowType)) { - il.Emit (OpCodes.Ldfld, typeof(Group).GetField ("children", BindingFlags.Instance | BindingFlags.NonPublic)); - il.Emit(OpCodes.Ldc_I4, dest[i+1].Index); - il.Emit (OpCodes.Callvirt, typeof(List).GetMethod("get_Item", new Type[] { typeof(Int32) })); - continue; - } - if (typeof(Container).IsAssignableFrom (dest[i].CrowType) || dest[i+1].Index < 0) { - il.Emit (OpCodes.Ldfld, typeof(PrivateContainer).GetField ("child", BindingFlags.Instance | BindingFlags.NonPublic)); - continue; - } - if (typeof(TemplatedContainer).IsAssignableFrom (dest[i].CrowType)) { - il.Emit (OpCodes.Callvirt, typeof(TemplatedContainer).GetProperty ("Content").GetGetMethod ()); - continue; - } - if (typeof(TemplatedGroup).IsAssignableFrom (dest[i].CrowType)) { - il.Emit (OpCodes.Callvirt, typeof(TemplatedGroup).GetProperty ("Items").GetGetMethod ()); - il.Emit(OpCodes.Ldc_I4, dest[i+1].Index); - il.Emit (OpCodes.Callvirt, typeof(List).GetMethod("get_Item", new Type[] { typeof(Int32) })); - continue; - } - } + if (orig.Count < dest.Count) { + for (int i = orig.Count - 1; i < dest.Count - 1; i++) + emitGetChild (il, dest [i].CrowType, dest [i + 1].Index); + } else { + for (int j = dest.Count; j < orig.Count; j++) + il.Emit (OpCodes.Callvirt, typeof(ILayoutable).GetProperty ("Parent").GetGetMethod ()); + } + } + void emitGetInstance (ILGenerator il, NodeAddress dest){ + for (int i = 0; i < dest.Count - 1; i++) + emitGetChild (il, dest [i].CrowType, dest [i + 1].Index); + } + void emitGetChild(ILGenerator il, Type parentType, int index){ + if (typeof (Group).IsAssignableFrom (parentType)) { + il.Emit (OpCodes.Ldfld, typeof(Group).GetField ("children", BindingFlags.Instance | BindingFlags.NonPublic)); + il.Emit(OpCodes.Ldc_I4, index); + il.Emit (OpCodes.Callvirt, typeof(List).GetMethod("get_Item", new Type[] { typeof(Int32) })); + return; + } + if (typeof(Container).IsAssignableFrom (parentType) || index < 0) { + il.Emit (OpCodes.Ldfld, typeof(PrivateContainer).GetField ("child", BindingFlags.Instance | BindingFlags.NonPublic)); + return; + } + if (typeof(TemplatedContainer).IsAssignableFrom (parentType)) { + il.Emit (OpCodes.Callvirt, typeof(TemplatedContainer).GetProperty ("Content").GetGetMethod ()); + return; + } + if (typeof(TemplatedGroup).IsAssignableFrom (parentType)) { + il.Emit (OpCodes.Callvirt, typeof(TemplatedGroup).GetProperty ("Items").GetGetMethod ()); + il.Emit(OpCodes.Ldc_I4, index); + il.Emit (OpCodes.Callvirt, typeof(List).GetMethod("get_Item", new Type[] { typeof(Int32) })); return; } - - for (int j = dest.Count; j < orig.Count; j++) - il.Emit(OpCodes.Callvirt, typeof(ILayoutable).GetProperty("Parent").GetGetMethod()); } - void emitConvert(ILGenerator il, Type origType, Type destType){ + void emitConvert(ILGenerator il, Type origType, Type destType){ if (destType == typeof (string)) il.Emit (OpCodes.Callvirt, CompilerServices.miObjToString); else if (origType.IsValueType) { if (destType != origType) il.Emit (OpCodes.Callvirt, CompilerServices.GetConvertMethod (destType)); else - il.Emit (OpCodes.Unbox_Any, destType); + il.Emit (OpCodes.Unbox_Any, destType);//TODO:double check this }else il.Emit (OpCodes.Castclass, destType); } @@ -723,7 +979,7 @@ namespace Crow else if (sourceValueType != sourceValueType) { il.Emit (OpCodes.Callvirt, CompilerServices.GetConvertMethod (sourceValueType)); } else - il.Emit (OpCodes.Unbox_Any, sourceValueType); + il.Emit (OpCodes.Unbox_Any, sourceValueType); } //public string GetImlSourcesCode(){ // try {