From: jpbruyere Date: Fri, 23 Dec 2016 14:33:58 +0000 (+0100) Subject: Compile style dynamic events X-Git-Tag: v0.5.1~63^2~9 X-Git-Url: https://git.osiis.dedyn.io/?a=commitdiff_plain;h=504c0bcf328f0e926112a4f21e22c218064cc300;p=jp%2Fcrow.git Compile style dynamic events --- diff --git a/src/CompilerServices/CompilerServices.cs b/src/CompilerServices/CompilerServices.cs index 38d4590c..dc95aca8 100644 --- a/src/CompilerServices/CompilerServices.cs +++ b/src/CompilerServices/CompilerServices.cs @@ -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 exps = new List(); + 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 (); + } + /// + /// Gets the node adress from binding expression splitted with '/' starting at a given node + /// + 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); + } + } } diff --git a/src/GraphicObjects/GraphicObject.cs b/src/GraphicObjects/GraphicObject.cs index b7913abd..6ca2466e 100644 --- a/src/GraphicObjects/GraphicObject.cs +++ b/src/GraphicObjects/GraphicObject.cs @@ -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)) { diff --git a/src/Instantiator.cs b/src/Instantiator.cs index e435c0ab..c681ff0a 100644 --- a/src/Instantiator.cs +++ b/src/Instantiator.cs @@ -39,10 +39,13 @@ namespace Crow /// 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 exps = new List(); - 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 (); - } - /// - /// Gets the node adress from binding expression splitted with '/' starting at a given node - /// - 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); - } /// /// Splits the binding expression /// @@ -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); } /// Emits handler method bindings