aboutsummaryrefslogtreecommitdiffstatsci
path: root/src/CompilerServices/CompilerServices.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/CompilerServices/CompilerServices.cs')
-rw-r--r--src/CompilerServices/CompilerServices.cs249
1 files changed, 249 insertions, 0 deletions
diff --git a/src/CompilerServices/CompilerServices.cs b/src/CompilerServices/CompilerServices.cs
index 0b69f970..ebb365c9 100644
--- a/src/CompilerServices/CompilerServices.cs
+++ b/src/CompilerServices/CompilerServices.cs
@@ -5,12 +5,261 @@ using System.Diagnostics;
using System.Linq;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
+using System.Xml;
namespace Crow
{
public static class CompilerServices
{
+ public static void BuildInstanciator(IMLInstantiatorBuilder builder, Type crowType){
+ string tmpXml = builder.ReadOuterXml ();
+
+ builder.il.Emit (OpCodes.Ldloc_0);//save current go onto the stack if child has to be added
+
+ if (typeof(TemplatedControl).IsAssignableFrom (crowType)) {
+ //if its a template, first read template elements
+ using (IMLInstantiatorBuilder reader = new IMLInstantiatorBuilder (builder.il, tmpXml)) {
+
+ string template = reader.GetAttribute ("Template");
+
+ bool inlineTemplate = false;
+ if (string.IsNullOrEmpty (template)) {
+ reader.Read ();
+
+ while (reader.Read ()) {
+ if (!reader.IsStartElement ())
+ continue;
+ if (reader.Name == "Template") {
+ inlineTemplate = true;
+ reader.Read ();
+
+ readChildren (reader, crowType);
+ continue;
+ }
+ }
+ if (!inlineTemplate) {
+ DefaultTemplate dt = (DefaultTemplate)crowType.GetCustomAttributes (typeof(DefaultTemplate), true).FirstOrDefault();
+ template = dt.Path;
+ }
+ }
+ if (!inlineTemplate) {
+ reader.il.Emit (OpCodes.Ldloc_0);//Load this templateControl ref
+
+ reader.il.Emit (OpCodes.Ldstr, template); //Load template path string
+ reader.il.Emit (OpCodes.Callvirt,//call Interface.Load(path)
+ typeof(Interface).GetMethod ("Load", BindingFlags.Static | BindingFlags.Public));
+ reader.il.Emit (OpCodes.Callvirt,//add child
+ typeof(PrivateContainer).GetMethod ("SetChild", BindingFlags.Instance | BindingFlags.NonPublic));
+ }
+ }
+ }
+
+ using (IMLInstantiatorBuilder reader = new IMLInstantiatorBuilder(builder.il,tmpXml)){
+ reader.Read ();
+
+ if (reader.HasAttributes) {
+ string style = reader.GetAttribute ("Style");
+ if (!string.IsNullOrEmpty (style)) {
+ PropertyInfo pi = crowType.GetProperty ("Style");
+ CompilerServices.EmitSetValue (reader.il, pi, style);
+ }
+ }
+ if (reader.HasAttributes) {
+ reader.il.Emit (OpCodes.Ldloc_0);
+ reader.il.Emit (OpCodes.Callvirt, typeof(GraphicObject).GetMethod ("loadDefaultValues"));
+
+ while (reader.MoveToNextAttribute ()) {
+ if (reader.Name == "Style")
+ continue;
+
+ PropertyInfo pi = crowType.GetProperty (reader.Name);
+
+ if (pi == null)
+ throw new Exception ("Member '" + reader.Name + "' not found in " + crowType.Name);
+
+ if (reader.Value.StartsWith("{"))
+ Debug.WriteLine("Binding => " + pi.Name + ": " + reader.Value);
+ else
+ CompilerServices.EmitSetValue (reader.il, pi, reader.Value);
+
+ }
+ reader.MoveToElement ();
+ }
+
+ if (reader.IsEmptyElement) {
+ reader.il.Emit (OpCodes.Pop);//pop saved ref to current object
+ return;
+ }
+
+ readChildren (reader, crowType);
+ }
+ builder.il.Emit (OpCodes.Pop);//pop saved ref to current object
+ }
+ static void readChildren(IMLInstantiatorBuilder reader, Type crowType){
+ MethodInfo miAddChild = null;
+ bool endTagReached = false;
+ while (reader.Read()){
+ switch (reader.NodeType) {
+ case XmlNodeType.EndElement:
+ endTagReached = true;
+ break;
+ case XmlNodeType.Element:
+ //Templates
+
+
+ if (miAddChild == null) {
+ if (typeof(Group).IsAssignableFrom (crowType))
+ miAddChild = typeof(Group).GetMethod ("AddChild");
+ else if (typeof(Container).IsAssignableFrom (crowType))
+ miAddChild = typeof(Container).GetMethod ("SetChild");
+ else if (typeof(PrivateContainer).IsAssignableFrom (crowType))
+ miAddChild = typeof(PrivateContainer).GetMethod ("SetChild",
+ BindingFlags.Instance | BindingFlags.NonPublic);
+ }
+
+ //push current instance on stack for parenting
+ //loc_0 will be used for child
+ reader.il.Emit (OpCodes.Ldloc_0);
+
+ Type t = Type.GetType ("Crow." + reader.Name);
+ if (t == null) {
+ Assembly a = Assembly.GetEntryAssembly ();
+ foreach (Type expT in a.GetExportedTypes ()) {
+ if (expT.Name == reader.Name)
+ t = expT;
+ }
+ }
+ if (t == null)
+ throw new Exception (reader.Name + " type not found");
+
+ reader.il.Emit(OpCodes.Newobj, t.GetConstructors () [0]);
+ reader.il.Emit (OpCodes.Stloc_0);//child is now loc_0
+
+ BuildInstanciator(reader, t);
+
+ reader.il.Emit (OpCodes.Ldloc_0);//load child on stack for parenting
+ reader.il.Emit (OpCodes.Callvirt, miAddChild);
+ reader.il.Emit (OpCodes.Stloc_0); //reset local to current go
+ reader.il.Emit (OpCodes.Ldloc_0);//save current go onto the stack if child has to be added
+ break;
+ }
+ if (endTagReached)
+ break;
+ }
+ }
+ public static void EmitSetValue(ILGenerator il, PropertyInfo pi, object val){
+ il.Emit (OpCodes.Ldloc_0);
+
+ if (val == null) {
+ il.Emit (OpCodes.Ldnull);
+ il.Emit (OpCodes.Callvirt, pi.GetSetMethod ());
+ return;
+ }
+ Type dvType = val.GetType ();
+
+ if (dvType.IsValueType) {
+ if (pi.PropertyType.IsValueType) {
+ if (pi.PropertyType.IsEnum) {
+ if (pi.PropertyType != dvType)
+ throw new Exception ("Enum mismatch in default values: " + pi.PropertyType.FullName);
+ il.Emit (OpCodes.Ldc_I4, Convert.ToInt32 (val));
+ } else {
+ switch (Type.GetTypeCode (dvType)) {
+ case TypeCode.Boolean:
+ if ((bool)val == true)
+ il.Emit (OpCodes.Ldc_I4_1);
+ else
+ il.Emit (OpCodes.Ldc_I4_0);
+ break;
+ // case TypeCode.Empty:
+ // break;
+ // case TypeCode.Object:
+ // break;
+ // case TypeCode.DBNull:
+ // break;
+ // case TypeCode.SByte:
+ // break;
+ // case TypeCode.Decimal:
+ // break;
+ // case TypeCode.DateTime:
+ // break;
+ case TypeCode.Char:
+ il.Emit (OpCodes.Ldc_I4, Convert.ToChar (val));
+ break;
+ case TypeCode.Byte:
+ case TypeCode.Int16:
+ case TypeCode.Int32:
+ il.Emit (OpCodes.Ldc_I4, Convert.ToInt32 (val));
+ break;
+ case TypeCode.UInt16:
+ case TypeCode.UInt32:
+ il.Emit (OpCodes.Ldc_I4, Convert.ToUInt32 (val));
+ break;
+ case TypeCode.Int64:
+ il.Emit (OpCodes.Ldc_I8, Convert.ToInt64 (val));
+ break;
+ case TypeCode.UInt64:
+ il.Emit (OpCodes.Ldc_I8, Convert.ToUInt64 (val));
+ break;
+ case TypeCode.Single:
+ il.Emit (OpCodes.Ldc_R4, Convert.ToSingle (val));
+ break;
+ case TypeCode.Double:
+ il.Emit (OpCodes.Ldc_R8, Convert.ToDouble (val));
+ break;
+ case TypeCode.String:
+ il.Emit (OpCodes.Ldstr, Convert.ToString (val));
+ break;
+ default:
+ il.Emit (OpCodes.Pop);
+ return;
+ }
+ }
+ } else
+ throw new Exception ("Expecting valuetype in default values for: " + pi.Name);
+ }else{
+ //surely a class or struct
+ if (dvType != typeof(string))
+ throw new Exception ("Expecting String in default values for: " + pi.Name);
+ if (pi.PropertyType == typeof(string))
+ il.Emit (OpCodes.Ldstr, Convert.ToString (val));
+ else if (pi.PropertyType.IsEnum) {
+ MethodInfo miParse = typeof(Enum).GetMethod
+ ("Parse", BindingFlags.Static | BindingFlags.Public,
+ Type.DefaultBinder, new Type [] {typeof (Type), typeof (string), typeof (bool)}, null);
+
+ if (miParse == null)
+ throw new Exception ("Enum Parse method not found");
+
+ //load type of enum
+ il.Emit(OpCodes.Ldtoken, pi.PropertyType);
+ il.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle", new
+ Type[1]{typeof(RuntimeTypeHandle)}));
+ //load enum value name
+ il.Emit (OpCodes.Ldstr, Convert.ToString (val));//TODO:is this convert required?
+ //load false
+ il.Emit (OpCodes.Ldc_I4_0);
+ il.Emit (OpCodes.Callvirt, miParse);
+
+ if (miParse.ReturnType != pi.PropertyType)
+ il.Emit (OpCodes.Unbox_Any, pi.PropertyType);
+ } else {
+ MethodInfo miParse = pi.PropertyType.GetMethod
+ ("Parse", BindingFlags.Static | BindingFlags.Public,
+ Type.DefaultBinder, new Type [] {typeof (string)},null);
+ if (miParse == null)
+ throw new Exception ("no Parse method found for: " + pi.PropertyType.FullName);
+
+ il.Emit (OpCodes.Ldstr, Convert.ToString (val));//TODO:is this convert required?
+ il.Emit (OpCodes.Callvirt, miParse);
+
+ if (miParse.ReturnType != pi.PropertyType)
+ il.Emit (OpCodes.Unbox_Any, pi.PropertyType);
+ }
+ }
+ il.Emit (OpCodes.Callvirt, pi.GetSetMethod ());
+ }
public static void ResolveBindings (List<Binding> Bindings)
{
if (Bindings == null)