aboutsummaryrefslogtreecommitdiffstatsci
path: root/src/IMLReader.cs
diff options
context:
space:
mode:
authorjpbruyere <jp.bruyere@hotmail.com>2016-08-09 23:59:33 +0200
committerjpbruyere <jp.bruyere@hotmail.com>2016-08-10 02:26:00 +0200
commit5a03df5faeda15e2d99c4c656eb62ed21817fdf6 (patch)
treedd2b4451e30c7532b00970061fae751dcda6b8d8 /src/IMLReader.cs
parent19b2404da8c1c8723bb79e58787cd37386d01139 (diff)
move MSIL generator from IML into IMLReader.
modifié : src/CompilerServices/CompilerServices.cs rename : src/IMLInstantiatorBuilder.cs => src/IMLReader.cs modifié : src/Interface.cs modifié : Crow.csproj
Diffstat (limited to 'src/IMLReader.cs')
-rw-r--r--src/IMLReader.cs287
1 files changed, 287 insertions, 0 deletions
diff --git a/src/IMLReader.cs b/src/IMLReader.cs
new file mode 100644
index 00000000..c1238081
--- /dev/null
+++ b/src/IMLReader.cs
@@ -0,0 +1,287 @@
+//
+// IMLReader.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// Copyright (c) 2016 jp
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+using System;
+using System.Xml;
+using System.IO;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.Linq;
+
+namespace Crow
+{
+ public class IMLReader : XmlTextReader
+ {
+ public string ImlPath;
+ public Stream ImlStream;
+ public Type RootType = null;
+
+ DynamicMethod dm = null;
+ public ILGenerator il = null;
+
+ #region CTOR
+ public IMLReader (string path)
+ : this(Interface.GetStreamFromPath (path)){
+ ImlPath = path;
+ }
+ public IMLReader (Stream stream)
+ : base(stream)
+ {
+ ImlStream = stream;
+ readRootType();
+ InitEmitter();
+ BuildInstanciator(RootType);
+ Read();//close tag
+ }
+ public IMLReader (ILGenerator ilGen, string xmlFragment)
+ : base(xmlFragment, XmlNodeType.Element,null){
+ il = ilGen;
+ }
+ #endregion
+
+ /// <summary>
+ /// Finalize instatiator MSIL and return LoaderInvoker delegate
+ /// </summary>
+ public Instantiator GetInstanciator(){
+ il.Emit(OpCodes.Ret);
+
+ return new Instantiator (RootType,
+ (Interface.LoaderInvoker)dm.CreateDelegate (typeof(Interface.LoaderInvoker)));
+ }
+
+ /// <summary>
+ /// Inits il generator, RootType must have been read first
+ /// </summary>
+ void InitEmitter(){
+
+ dm = new DynamicMethod("dyn_instantiator",
+ MethodAttributes.Family | MethodAttributes.FamANDAssem | MethodAttributes.NewSlot,
+ CallingConventions.Standard,
+ typeof(void),new Type[] {typeof(object)}, RootType, true);
+
+ il = dm.GetILGenerator(256);
+ il.DeclareLocal(typeof(GraphicObject));
+ il.Emit(OpCodes.Nop);
+ //set local GraphicObject to root object passed as 1st argument
+ il.Emit (OpCodes.Ldarg_0);
+ il.Emit (OpCodes.Stloc_0);
+ }
+ void BuildInstanciator(Type crowType){
+ string tmpXml = ReadOuterXml ();
+
+ 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 (IMLReader reader = new IMLReader (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;
+ }else if (reader.Name == "ItemTemplate") {
+ reader.Skip ();
+ // string dataType = "default", datas = "", itemTmp;
+ // while (reader.MoveToNextAttribute ()) {
+ // if (reader.Name == "DataType")
+ // dataType = reader.Value;
+ // else if (reader.Name == "Data")
+ // datas = reader.Value;
+ // }
+ //
+ // reader.Read();
+ // itemTmp = .ReadInnerXml ();
+ //
+ // if (ItemTemplates == null)
+ // ItemTemplates = new Dictionary<string, ItemTemplate> ();
+ // //TODO:check encoding
+ // ItemTemplates[dataType] = new ItemTemplate (Encoding.UTF8.GetBytes(itemTmp));
+ // if (!string.IsNullOrEmpty (datas))
+ // ItemTemplates [dataType].CreateExpandDelegate(this, dataType, datas);
+
+ 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
+ crowType.GetMethod ("loadTemplate", BindingFlags.Instance | BindingFlags.NonPublic));
+ }
+ }
+
+ using (IMLReader reader = new IMLReader(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"));
+
+ MethodInfo miAddBinding = typeof(GraphicObject).GetMethod ("BindMember");
+
+ while (reader.MoveToNextAttribute ()) {
+ if (reader.Name == "Style")
+ continue;
+
+ MemberInfo mi = crowType.GetMember (reader.Name).FirstOrDefault();
+ if (mi == null)
+ throw new Exception ("Member '" + reader.Name + "' not found in " + crowType.Name);
+ if (mi.MemberType == MemberTypes.Event) {
+ CompilerServices.emitBindingCreation (reader.il, reader.Name, reader.Value);
+ continue;
+ }
+ PropertyInfo pi = mi as PropertyInfo;
+ if (pi == null)
+ throw new Exception ("Member '" + reader.Name + "' not found in " + crowType.Name);
+
+ if (reader.Value.StartsWith ("{")) {
+ CompilerServices.emitBindingCreation (reader.il, reader.Name, reader.Value.Substring (1, reader.Value.Length - 2));
+ }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);
+ }
+ il.Emit (OpCodes.Pop);//pop saved ref to current object
+ }
+ /// <summary>
+ /// Parse child node an generate corresponding msil
+ /// </summary>
+ void readChildren(IMLReader 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 (reader.Name == "Template" ||
+ reader.Name == "ItemTemplate") {
+ reader.Skip ();
+ continue;
+ }
+
+ 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(TemplatedContainer).IsAssignableFrom (crowType))
+ miAddChild = typeof(TemplatedContainer).GetProperty("Content").GetSetMethod();
+ 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
+
+ reader.BuildInstanciator(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;
+ }
+ }
+ /// <summary>
+ /// read first node to set GraphicObject class for loading
+ /// and let reader position on that node
+ /// </summary>
+ Type readRootType ()
+ {
+ string root = "Object";
+ while (Read()) {
+ if (NodeType == XmlNodeType.Element) {
+ root = this.Name;
+ break;
+ }
+ }
+
+ Type t = Type.GetType ("Crow." + root);
+ if (t == null) {
+ Assembly a = Assembly.GetEntryAssembly ();
+ foreach (Type expT in a.GetExportedTypes ()) {
+ if (expT.Name == root)
+ t = expT;
+ }
+ }
+ RootType = t;
+ return t;
+ }
+ }
+}
+