aboutsummaryrefslogtreecommitdiffstatsci
path: root/src/CompilerServices
diff options
context:
space:
mode:
authorjpbruyere <jp.bruyere@hotmail.com>2016-12-23 15:33:58 +0100
committerjpbruyere <jp.bruyere@hotmail.com>2016-12-23 15:33:58 +0100
commit504c0bcf328f0e926112a4f21e22c218064cc300 (patch)
tree4537d5c92e447e5d041addc95a8f4f71e3fe061d /src/CompilerServices
parentdecb4506152e9acd51200a9fb99076eaa4a78f63 (diff)
Compile style dynamic events
Diffstat (limited to 'src/CompilerServices')
-rw-r--r--src/CompilerServices/CompilerServices.cs168
1 files changed, 168 insertions, 0 deletions
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<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);
+ }
+
}
}