]> O.S.I.I.S - jp/crow.git/commitdiff
Compile style dynamic events
authorjpbruyere <jp.bruyere@hotmail.com>
Fri, 23 Dec 2016 14:33:58 +0000 (15:33 +0100)
committerjpbruyere <jp.bruyere@hotmail.com>
Fri, 23 Dec 2016 14:33:58 +0000 (15:33 +0100)
src/CompilerServices/CompilerServices.cs
src/GraphicObjects/GraphicObject.cs
src/Instantiator.cs

index 38d4590cb0c1a879154348b3215f9d96c94d29cb..dc95aca86dca64e1a1a0b78989d41d45426e8c45 100644 (file)
@@ -819,6 +819,174 @@ namespace Crow
                                }
                        }
                }
+               public static Delegate compileDynEventHandler(EventInfo sourceEvent, string expression, NodeAddress currentNode = null){
+                       Type lopType = null;
+
+                       if (currentNode == null)
+                               lopType = sourceEvent.DeclaringType;
+                       else
+                               lopType = currentNode.NodeType;
+
+                       #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);
+                       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 ('/');
+                               MemberInfo lopMI = null;
+
+                               il.Emit (OpCodes.Ldarg_0);  //load sender ref onto the stack
+
+                               if (lopParts.Length > 1) {
+                                       NodeAddress lopNA = getNodeAdressFromBindingExp (currentNode, lopParts);
+                                       CompilerServices.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);
+
+                       return dm.CreateDelegate (sourceEvent.EventHandlerType);
+               }
+               public static string[] splitOnSemiColumnOutsideAccolades (string expression){
+                       List<String> exps = new List<string>();
+                       int accCount = 0;
+                       int expPtr = 0;
+                       for (int c = 0; c < expression.Length; c++) {
+                               switch (expression[c]){
+                               case '{':
+                                       accCount++;
+                                       break;
+                               case '}':
+                                       accCount--;
+                                       break;
+                               case ';':
+                                       if (accCount > 0)
+                                               break;
+                                       exps.Add(expression.Substring(expPtr, c - expPtr - 1));
+                                       expPtr = c + 1;
+                                       break;
+                               }
+                       }
+                       if (exps.Count == 0)
+                               exps.Add(expression);
+                       return exps.ToArray ();
+               }
+               /// <summary>
+               /// Gets the node adress from binding expression splitted with '/' starting at a given node
+               /// </summary>
+               public static NodeAddress getNodeAdressFromBindingExp(NodeAddress sourceAddr, string[] bindingExp){
+                       int ptr = sourceAddr.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 (sourceAddr [ptr].CrowType))
+                                               break;
+                                       ptr--;
+                               }
+                       } else if (bindingExp [0] == "..") { //search starting at current node
+                               int levelUp = bindingExp.Length - 1;
+                               if (levelUp > ptr + 1)
+                                       throw new Exception ("Binding error: try to bind outside IML source");
+                               ptr -= levelUp;
+                       }
+                       Node[] targetNode = new Node[ptr+1];
+                       Array.Copy (sourceAddr.ToArray (), targetNode, ptr + 1);
+                       return new NodeAddress (targetNode);
+               }
+
        }
 }
 
index b7913abd33f979dc810ce608d44c597c1549b3ed..6ca2466e22103442cb5e430219758f1309131d20 100644 (file)
@@ -135,12 +135,12 @@ namespace Crow
                }
                [XmlIgnore]public ILayoutable LogicalParent {
                        get { return logicalParent == null ? Parent : logicalParent; }
-                       set { 
+                       set {
                                if (logicalParent == value)
                                        return;
                                if (logicalParent != null)
                                        (logicalParent as GraphicObject).DataSourceChanged -= onLogicalParentDataSourceChanged;
-                               logicalParent = value; 
+                               logicalParent = value;
                                if (logicalParent != null)
                                        (logicalParent as GraphicObject).DataSourceChanged += onLogicalParentDataSourceChanged;
                        }
@@ -563,13 +563,13 @@ namespace Crow
                }
                internal bool localDataSourceIsNull { get { return dataSource == null; } }
 
-               public virtual void OnDataSourceChanged(object sender, DataSourceChangeEventArgs e){                    
+               public virtual void OnDataSourceChanged(object sender, DataSourceChangeEventArgs e){
                        DataSourceChanged.Raise (this, e);
                        #if DEBUG_BINDING
                        Debug.WriteLine("New DataSource for => {0} \n\t{1}=>{2}", this.ToString(),e.OldDataSource,e.NewDataSource);
                        #endif
                }
-                       
+
                [XmlAttributeAttribute]
                public virtual string Style {
                        get { return style; }
@@ -664,7 +664,26 @@ namespace Crow
                                string expression;
                                if (!getDefaultEvent(ei, styling, out expression))
                                        continue;
-                               //CompilerServices.emitBindingCreation (il, ei.Name, expression);
+                               //TODO:dynEventHandler could be cached somewhere, maybe a style instanciato class holding the styling delegate and bound to it.
+                               foreach (string exp in CompilerServices.splitOnSemiColumnOutsideAccolades(expression)) {
+                                       string trimed = exp.Trim();
+                                       if (trimed.StartsWith ("{", StringComparison.OrdinalIgnoreCase)){
+                                               il.Emit (OpCodes.Ldloc_0);//load this as 1st arg of event Add
+
+                                               //push eventInfo as 1st arg of compile
+                                               il.Emit (OpCodes.Ldloc_0);
+                                               il.Emit (OpCodes.Call, typeof(object).GetMethod("GetType"));
+                                               il.Emit (OpCodes.Ldstr, ei.Name);//push event name
+                                               il.Emit (OpCodes.Call, typeof(Type).GetMethod("GetEvent", new Type[] {typeof(string)}));
+                                               //push expression as 2nd arg of compile
+                                               il.Emit (OpCodes.Ldstr, trimed.Substring (1, trimed.Length - 2));
+                                               il.Emit (OpCodes.Ldnull);
+                                               il.Emit (OpCodes.Callvirt, typeof(CompilerServices).GetMethod ("compileDynEventHandler", BindingFlags.Static | BindingFlags.Public));
+                                               il.Emit (OpCodes.Castclass, ei.EventHandlerType);
+                                               il.Emit (OpCodes.Callvirt, ei.AddMethod);
+                                       }else
+                                               Debug.WriteLine("error in styling, event not handled : " + trimed);
+                               }
                        }
 
                        foreach (PropertyInfo pi in thisType.GetProperties(BindingFlags.Public | BindingFlags.Instance)) {
index e435c0abe5f2e6b0b16bd25931fd0ea3cfe5ee5c..c681ff0a5a58b52b1797741bc26a5badf8c2a305 100644 (file)
@@ -39,10 +39,13 @@ namespace Crow
        /// </summary>
        public class Instantiator
        {
+               #region Dynamic Method ID generation
                static long curId = 0;
                internal static long NewId {
                        get { return curId++; }
                }
+               #endregion
+
                public Type RootType;
                InstanciatorInvoker loader;
 
@@ -52,7 +55,6 @@ namespace Crow
                public Instantiator (string path) : this (Interface.GetStreamFromPath(path)) {
                        sourcePath = path;
                }
-
                public Instantiator (Stream stream)
                {
 #if DEBUG_LOAD
@@ -261,7 +263,7 @@ namespace Crow
                                                        throw new Exception ("Member '" + reader.Name + "' not found in " + ctx.CurrentNodeType.Name);
 
                                                if (mi.MemberType == MemberTypes.Event) {
-                                                       foreach (string exp in splitOnSemiColumnOutsideAccolades(reader.Value)) {
+                                                       foreach (string exp in CompilerServices.splitOnSemiColumnOutsideAccolades(reader.Value)) {
                                                                string trimed = exp.Trim();
                                                                if (trimed.StartsWith ("{", StringComparison.OrdinalIgnoreCase))
                                                                        compileAndStoreDynHandler (ctx, mi as EventInfo, trimed.Substring (1, trimed.Length - 2));
@@ -363,56 +365,6 @@ namespace Crow
                                ctx.StorePropertyBinding (bindingDef);
                }
 
-               string[] splitOnSemiColumnOutsideAccolades (string expression){
-                       List<String> exps = new List<string>();
-                       int accCount = 0;
-                       int expPtr = 0;
-                       for (int c = 0; c < expression.Length; c++) {
-                               switch (expression[c]){
-                               case '{':
-                                       accCount++;
-                                       break;
-                               case '}':
-                                       accCount--;
-                                       break;
-                               case ';':
-                                       if (accCount > 0)
-                                               break;
-                                       exps.Add(expression.Substring(expPtr, c - expPtr - 1));
-                                       expPtr = c + 1;
-                                       break;
-                               }
-                       }
-                       if (exps.Count == 0)
-                               exps.Add(expression);
-                       return exps.ToArray ();
-               }
-               /// <summary>
-               /// Gets the node adress from binding expression splitted with '/' starting at a given node
-               /// </summary>
-               NodeAddress getNodeAdressFromBindingExp(NodeAddress sourceAddr, string[] bindingExp){
-                       int ptr = sourceAddr.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 (sourceAddr [ptr].CrowType))
-                                               break;
-                                       ptr--;
-                               }
-                       } else if (bindingExp [0] == "..") { //search starting at current node
-                               int levelUp = bindingExp.Length - 1;
-                               if (levelUp > ptr + 1)
-                                       throw new Exception ("Binding error: try to bind outside IML source");
-                               ptr -= levelUp;
-                       }
-                       Node[] targetNode = new Node[ptr+1];
-                       Array.Copy (sourceAddr.ToArray (), targetNode, ptr + 1);
-                       return new NodeAddress (targetNode);
-               }
                /// <summary>
                /// Splits the binding expression
                /// </summary>
@@ -434,7 +386,7 @@ namespace Crow
                                string[] bindingExp = expression.Split ('/');
 
                                if (bindingExp.Length > 1)
-                                       bindingDef.TargetNA = getNodeAdressFromBindingExp (sourceNA, bindingExp);
+                                       bindingDef.TargetNA = CompilerServices.getNodeAdressFromBindingExp (sourceNA, bindingExp);
 
                                string [] bindTrg = bindingExp.Last().Split ('.');
 
@@ -469,124 +421,9 @@ namespace Crow
                        #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;
-
-                       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);
-                                       CompilerServices.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 = cachedDelegates.Count;
-                       cachedDelegates.Add (dm.CreateDelegate (sourceEvent.EventHandlerType));
+                       cachedDelegates.Add (CompilerServices.compileDynEventHandler (sourceEvent, expression, ctx.CurrentNodeAddress));
                        ctx.emitCachedDelegateHandlerAddition(dmIdx, sourceEvent);
                }
                /// <summary> Emits handler method bindings </summary>