32ae82592dd774a9d84c5275df8c8079d5948c4b
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.XamlBuild / src / public / XamlBuild / XamlCTask.cs
1 /*
2  * Copyright(c) 2022 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 using System;
18 using System.Collections.Generic;
19 using System.IO;
20 using System.Linq;
21 using System.Text;
22 using System.Xml;
23 using System.ComponentModel;
24 using Mono.Cecil;
25 using Mono.Cecil.Cil;
26
27 using Tizen.NUI.Binding;
28 using Tizen.NUI.EXaml;
29 using Tizen.NUI.EXaml.Build.Tasks;
30 using static Microsoft.Build.Framework.MessageImportance;
31 using static Mono.Cecil.Cil.OpCodes;
32
33 namespace Tizen.NUI.Xaml.Build.Tasks
34 {
35     [EditorBrowsable(EditorBrowsableState.Never)]
36     public class XamlCTask : XamlTask
37     {
38         bool hasCompiledXamlResources;
39         public bool KeepXamlResources { get; set; }
40         public bool OptimizeIL { get; set; }
41
42         [Obsolete("OutputGeneratedILAsCode is obsolete as of version 2.3.4. This option is no longer available.")]
43         public bool OutputGeneratedILAsCode { get; set; }
44
45         public bool CompileByDefault { get; set; }
46         public bool ForceCompile { get; set; }
47
48         public bool UseInjection { get; set; }
49
50         public int XamlOptimization { get; set; } = 2;
51
52         public IAssemblyResolver DefaultAssemblyResolver { get; set; }
53
54         public string Type { get; set; }
55         public MethodDefinition InitCompForType { get; private set; }
56         internal bool ReadOnly { get; set; }
57
58         public string outputRootPath { get; set; }
59
60         public bool PrintReferenceAssemblies { get; set; }
61
62         private void PrintParam(string logFileName, string log)
63         {
64             FileStream stream = null;
65             if (false == File.Exists(logFileName))
66             {
67                 stream = File.Create(logFileName);
68             }
69             else
70             {
71                 stream = File.Open(logFileName, FileMode.Append);
72             }
73
74             byte[] buffer = System.Text.Encoding.Default.GetBytes(log + "\n");
75             stream.Write(buffer, 0, buffer.Length);
76             stream.Close();
77         }
78
79         private void PrintParam(string logFileName)
80         {
81             FileStream stream = File.Create(logFileName);
82
83             string str = "Assembly is " + Assembly + "\n";
84             str += "DependencyPaths is " + DependencyPaths + "\n";
85             str += "ReferencePath is " + ReferencePath + "\n";
86             str += "DebugType is " + DebugType + "\n";
87             str += "Type is " + Type + "\n";
88             str += "ReadOnly is " + ReadOnly + "\n";
89
90             byte[] buffer = Encoding.Default.GetBytes(str);
91             stream.Write(buffer, 0, buffer.Length);
92
93             stream.Close();
94         }
95
96         static private TypeDefinition baseTypeDefiniation = null;
97         static public TypeDefinition BaseTypeDefiniation
98         {
99             get
100             {
101                 return baseTypeDefiniation;
102             }
103         }
104
105         private void GatherAssemblyInfo(string p)
106         {
107             try
108             {
109                 ModuleDefinition module = ModuleDefinition.ReadModule(p);
110
111                 if (null == baseTypeDefiniation)
112                 {
113                     baseTypeDefiniation = module.GetType("Tizen.NUI.Binding.BindableObject");
114                 }
115
116                 foreach (var attr in module.Assembly.CustomAttributes)
117                 {
118                     if (attr.AttributeType.FullName == "Tizen.NUI.XmlnsDefinitionAttribute")
119                     {
120                         string xmlNamespace = attr.ConstructorArguments[0].Value as string;
121                         string clrNamespace = attr.ConstructorArguments[1].Value as string;
122
123                         int level = 0;
124                         string assemblyName = module.Assembly.FullName;
125
126                         if (true == attr.HasProperties)
127                         {
128                             foreach (var property in attr.Properties)
129                             {
130                                 if ("Level" == property.Name)
131                                 {
132                                     level = int.Parse(property.Argument.Value.ToString());
133                                 }
134                                 if ("AssemblyName" == property.Name)
135                                 {
136                                     assemblyName = property.Argument.Value as string;
137                                 }
138                             }
139                         }
140
141                         XmlnsDefinitionAttribute attribute = new XmlnsDefinitionAttribute(xmlNamespace, clrNamespace, level);
142                         attribute.AssemblyName = assemblyName;
143                         s_xmlnsDefinitions.Add(attribute);
144                     }
145                 }
146
147                 module.Dispose();
148             }
149             catch (Exception e)
150             {
151                 int temp = 0;
152             }
153         }
154
155         public override bool Execute(out IList<Exception> thrownExceptions)
156         {
157             if (true == PrintReferenceAssemblies)
158             {
159                 PrintParam(@"XamlC_Log.txt", "ReferencePath is " + ReferencePath);
160             }
161
162             LoggingHelper.LogWarning("Assembly is " + Assembly);
163
164             thrownExceptions = null;
165
166             LoggingHelper.LogMessage(Normal, $"{new string(' ', 0)}Compiling Xaml, assembly: {Assembly}");
167             var skipassembly = !CompileByDefault;
168             bool success = true;
169
170             if (!File.Exists(Assembly))
171             {
172                 throw new Exception(String.Format("Assembly file {0} is not exist", Assembly));
173                 //LoggingHelper.LogMessage(Normal, $"{new string(' ', 2)}Assembly file not found. Skipping XamlC.");
174                 //return true;
175             }
176
177             s_xmlnsDefinitions.Clear();
178
179             var resolver = DefaultAssemblyResolver ?? new XamlCAssemblyResolver();
180             if (resolver is XamlCAssemblyResolver xamlCResolver)
181             {
182                 if (!string.IsNullOrEmpty(DependencyPaths))
183                 {
184                     foreach (var dep in DependencyPaths.Split(';'))
185                     {
186                         LoggingHelper.LogMessage(Low, $"{new string(' ', 2)}Adding searchpath {dep}");
187                         xamlCResolver.AddSearchDirectory(dep);
188                     }
189                 }
190
191                 if (!string.IsNullOrEmpty(ReferencePath))
192                 {
193                     var paths = ReferencePath.Replace("//", "/").Split(';');
194
195                     foreach (var p in paths)
196                     {
197                         GatherAssemblyInfo(p);
198
199                         var searchpath = System.IO.Path.GetDirectoryName(p);
200                         LoggingHelper.LogMessage(Low, $"{new string(' ', 2)}Adding searchpath {searchpath}");
201                         xamlCResolver.AddSearchDirectory(searchpath);
202                     }
203                 }
204             }
205             else
206                 LoggingHelper.LogMessage(Low, $"{new string(' ', 2)}Ignoring dependency and reference paths due to an unsupported resolver");
207
208             var readerParameters = new ReaderParameters
209             {
210                 AssemblyResolver = resolver,
211                 ReadWrite = !ReadOnly,
212                 ReadSymbols = NeedDebug,
213             };
214
215             using (var assemblyDefinition = AssemblyDefinition.ReadAssembly(System.IO.Path.GetFullPath(Assembly), readerParameters))
216             {
217                 if (null != XamlFilePath)
218                 {
219                     return GenerateEXaml(XamlFilePath, assemblyDefinition.MainModule, out thrownExceptions);
220                 }
221
222                 CustomAttribute xamlcAttr;
223                 if (assemblyDefinition.HasCustomAttributes &&
224                     (xamlcAttr =
225                         assemblyDefinition.CustomAttributes.FirstOrDefault(
226                             ca => ca.AttributeType.FullName == "Tizen.NUI.Xaml.XamlCompilationAttribute")) != null)
227                 {
228                     var options = (XamlCompilationOptions)xamlcAttr.ConstructorArguments[0].Value;
229                     if ((options & XamlCompilationOptions.Skip) == XamlCompilationOptions.Skip)
230                         skipassembly = true;
231                     if ((options & XamlCompilationOptions.Compile) == XamlCompilationOptions.Compile)
232                         skipassembly = false;
233                 }
234
235                 foreach (var module in assemblyDefinition.Modules)
236                 {
237                     var skipmodule = skipassembly;
238                     if (module.HasCustomAttributes &&
239                         (xamlcAttr =
240                             module.CustomAttributes.FirstOrDefault(
241                                 ca => ca.AttributeType.FullName == "Tizen.NUI.Xaml.XamlCompilationAttribute")) != null)
242                     {
243                         var options = (XamlCompilationOptions)xamlcAttr.ConstructorArguments[0].Value;
244                         if ((options & XamlCompilationOptions.Skip) == XamlCompilationOptions.Skip)
245                             skipmodule = true;
246                         if ((options & XamlCompilationOptions.Compile) == XamlCompilationOptions.Compile)
247                             skipmodule = false;
248                     }
249
250                     LoggingHelper.LogMessage(Low, $"{new string(' ', 2)}Module: {module.Name}");
251                     var resourcesToPrune = new List<EmbeddedResource>();
252                     foreach (var resource in module.Resources.OfType<EmbeddedResource>())
253                     {
254                         LoggingHelper.LogMessage(Low, $"{new string(' ', 4)}Resource: {resource.Name}");
255                         string classname;
256                         if (!resource.IsXaml(module, out classname))
257                         {
258                             LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}skipped.");
259                             continue;
260                         }
261                         TypeDefinition typeDef = module.GetType(classname);
262                         if (typeDef == null)
263                         {
264                             LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}no type found... skipped.");
265                             continue;
266                         }
267                         var skiptype = skipmodule;
268                         if (typeDef.HasCustomAttributes &&
269                             (xamlcAttr =
270                                 typeDef.CustomAttributes.FirstOrDefault(
271                                     ca => ca.AttributeType.FullName == "Tizen.NUI.Xaml.XamlCompilationAttribute")) != null)
272                         {
273                             var options = (XamlCompilationOptions)xamlcAttr.ConstructorArguments[0].Value;
274                             if ((options & XamlCompilationOptions.Skip) == XamlCompilationOptions.Skip)
275                                 skiptype = true;
276                             if ((options & XamlCompilationOptions.Compile) == XamlCompilationOptions.Compile)
277                                 skiptype = false;
278                         }
279
280                         if (Type != null)
281                             skiptype = !(Type == classname);
282
283                         if (skiptype && !ForceCompile)
284                         {
285                             LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}has XamlCompilationAttribute set to Skip and not Compile... skipped.");
286                             continue;
287                         }
288
289                         bool currentRetOfType = false;
290                         IList<Exception> currentExceptionsOfType = null;
291
292                         if(UseInjection) XamlOptimization = 1;
293                         LoggingHelper.LogWarning($"XamlOptimization is {XamlOptimization}.");
294                         if (0 == XamlOptimization)
295                         {//Use Xaml
296                             currentRetOfType = true;
297                         }
298                         else if (1 == XamlOptimization)
299                         {
300                             currentRetOfType = DoInjection(typeDef, resource, out currentExceptionsOfType);
301                         }
302                         else
303                         {
304                             currentRetOfType = GenerateEXaml(typeDef, resource, out currentExceptionsOfType);
305
306                             if (currentRetOfType)
307                             {
308                                 InjectionMethodGetEXamlPath(typeDef);
309                             }
310                         }
311
312                         if (null != currentExceptionsOfType)
313                         {
314                             if (null == thrownExceptions)
315                             {
316                                 thrownExceptions = new List<Exception>();
317                             }
318
319                             foreach (var e in currentExceptionsOfType)
320                             {
321                                 thrownExceptions.Add(e);
322                             }
323                         }
324
325                         if (false == currentRetOfType)
326                         {
327                             success = false;
328                             continue;
329                         }
330
331                         resourcesToPrune.Add(resource);
332                     }
333
334                     if (hasCompiledXamlResources)
335                     {
336                         LoggingHelper.LogMessage(Low, $"{new string(' ', 4)}Changing the module MVID");
337                         module.Mvid = Guid.NewGuid();
338                         LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}done.");
339                     }
340                     if (!KeepXamlResources)
341                     {
342                         if (resourcesToPrune.Any())
343                             LoggingHelper.LogMessage(Low, $"{new string(' ', 4)}Removing compiled xaml resources");
344                         foreach (var resource in resourcesToPrune)
345                         {
346                             LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Removing {resource.Name}");
347                             module.Resources.Remove(resource);
348                             LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}done.");
349                         }
350                     }
351                 }
352
353                 if (!hasCompiledXamlResources)
354                 {
355                     LoggingHelper.LogMessage(Low, $"{new string(' ', 0)}No compiled resources. Skipping writing assembly.");
356                     return success;
357                 }
358
359                 if (ReadOnly)
360                     return success;
361
362                 LoggingHelper.LogMessage(Low, $"{new string(' ', 0)}Writing the assembly");
363                 try
364                 {
365                     assemblyDefinition.Write(new WriterParameters
366                     {
367                         WriteSymbols = NeedDebug,
368                     });
369                     LoggingHelper.LogMessage(Low, $"{new string(' ', 2)}done.");
370                 }
371                 catch (Exception e)
372                 {
373                     LoggingHelper.LogMessage(Low, $"{new string(' ', 2)}failed.");
374                     LoggingHelper.LogErrorFromException(e);
375                     (thrownExceptions = thrownExceptions ?? new List<Exception>()).Add(e);
376                     LoggingHelper.LogMessage(Low, e.StackTrace);
377                     success = false;
378                 }
379             }
380             return success;
381         }
382
383         private string GetNameSpaceOfResource(EmbeddedResource resource)
384         {
385             var index = resource.Name.LastIndexOf('.');
386             var resourceNameWithoutSubfix = resource.Name.Substring(0, index);
387
388             index = resourceNameWithoutSubfix.LastIndexOf('.');
389             var nameSpace = resourceNameWithoutSubfix.Substring(0, index);
390
391             return nameSpace;
392         }
393
394         bool DoInjection(TypeDefinition typeDef, EmbeddedResource resource, out IList<Exception> thrownExceptions)
395         {
396             thrownExceptions = null;
397
398             var initComp = typeDef.Methods.FirstOrDefault(md => md.Name == "InitializeComponent");
399             if (initComp == null)
400             {
401                 LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}no InitializeComponent found... skipped.");
402                 return false;
403             }
404
405             CustomAttribute xamlFilePathAttr;
406             var xamlFilePath = typeDef.HasCustomAttributes && (xamlFilePathAttr = typeDef.CustomAttributes.FirstOrDefault(ca => ca.AttributeType.FullName == "Tizen.NUI.Xaml.XamlFilePathAttribute")) != null ?
407                                       (string)xamlFilePathAttr.ConstructorArguments[0].Value :
408                                       resource.Name;
409
410             var initCompRuntime = typeDef.Methods.FirstOrDefault(md => md.Name == "__InitComponentRuntime");
411             if (initCompRuntime != null)
412                 LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}__InitComponentRuntime already exists... not creating");
413             else
414             {
415                 LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Creating empty {typeDef.Name}.__InitComponentRuntime");
416                 initCompRuntime = new MethodDefinition("__InitComponentRuntime", initComp.Attributes, initComp.ReturnType);
417                 initCompRuntime.Body.InitLocals = true;
418                 LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}done.");
419                 LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Copying body of InitializeComponent to __InitComponentRuntime");
420                 initCompRuntime.Body = new MethodBody(initCompRuntime);
421                 var iCRIl = initCompRuntime.Body.GetILProcessor();
422                 foreach (var instr in initComp.Body.Instructions)
423                     iCRIl.Append(instr);
424                 initComp.Body.Instructions.Clear();
425                 initComp.Body.GetILProcessor().Emit(OpCodes.Ret);
426                 initComp.Body.InitLocals = true;
427
428                 typeDef.Methods.Add(initCompRuntime);
429                 LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}done.");
430             }
431
432             LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Parsing Xaml");
433             var rootnode = ParseXaml(resource.GetResourceStream(), typeDef);
434             if (rootnode == null)
435             {
436                 LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}failed.");
437                 return false;
438             }
439             LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}done.");
440
441             hasCompiledXamlResources = true;
442
443             LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Replacing {0}.InitializeComponent ()");
444             Exception e;
445
446             var embeddedResourceNameSpace = GetNameSpaceOfResource(resource);
447             if (!TryCoreCompile(initComp, rootnode, embeddedResourceNameSpace, out e))
448             {
449                 LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}failed.");
450                 (thrownExceptions = thrownExceptions ?? new List<Exception>()).Add(e);
451                 if (e is XamlParseException xpe)
452                     LoggingHelper.LogError(null, null, null, xamlFilePath, xpe.XmlInfo.LineNumber, xpe.XmlInfo.LinePosition, 0, 0, xpe.Message, xpe.HelpLink, xpe.Source);
453                 else if (e is XmlException xe)
454                     LoggingHelper.LogError(null, null, null, xamlFilePath, xe.LineNumber, xe.LinePosition, 0, 0, xe.Message, xe.HelpLink, xe.Source);
455                 else
456                     LoggingHelper.LogError(null, null, null, xamlFilePath, 0, 0, 0, 0, e.Message, e.HelpLink, e.Source);
457
458                 if (null != e.StackTrace)
459                 {
460                     LoggingHelper.LogMessage(Low, e.StackTrace);
461                 }
462
463                 return false;
464             }
465             if (Type != null)
466                 InitCompForType = initComp;
467
468             LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}done.");
469
470             if (OptimizeIL)
471             {
472                 LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Optimizing IL");
473                 initComp.Body.Optimize();
474                 LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}done.");
475             }
476
477 #pragma warning disable 0618
478             if (OutputGeneratedILAsCode)
479                 LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Decompiling option has been removed. Use a 3rd party decompiler to admire the beauty of the IL generated");
480 #pragma warning restore 0618
481
482             return true;
483         }
484
485         bool GenerateEXaml(TypeDefinition typeDef, EmbeddedResource resource, out IList<Exception> thrownExceptions)
486         {
487             thrownExceptions = null;
488
489             ModuleDefinition module = typeDef.Module;
490
491             CustomAttribute xamlFilePathAttr;
492             var xamlFilePath = typeDef.HasCustomAttributes && (xamlFilePathAttr = typeDef.CustomAttributes.FirstOrDefault(ca => ca.AttributeType.FullName == "Tizen.NUI.Xaml.XamlFilePathAttribute")) != null ?
493                                       (string)xamlFilePathAttr.ConstructorArguments[0].Value :
494                                       resource.Name;
495
496             LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Parsing Xaml");
497             var rootnode = ParseXaml(resource.GetResourceStream(), typeDef);
498             if (rootnode == null)
499             {
500                 LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}failed.");
501                 return false;
502             }
503             LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}done.");
504
505             hasCompiledXamlResources = true;
506
507             LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Replacing {0}.InitializeComponent ()");
508             Exception e;
509
510             var embeddedResourceNameSpace = GetNameSpaceOfResource(resource);
511             var visitorContext = new EXamlContext(typeDef, typeDef.Module, embeddedResourceNameSpace);
512
513             if (!TryCoreCompile(rootnode, visitorContext, out e))
514             {
515                 LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}failed.");
516                 (thrownExceptions = thrownExceptions ?? new List<Exception>()).Add(e);
517                 if (e is XamlParseException xpe)
518                     LoggingHelper.LogError(null, null, null, xamlFilePath, xpe.XmlInfo.LineNumber, xpe.XmlInfo.LinePosition, 0, 0, xpe.Message, xpe.HelpLink, xpe.Source);
519                 else if (e is XmlException xe)
520                     LoggingHelper.LogError(null, null, null, xamlFilePath, xe.LineNumber, xe.LinePosition, 0, 0, xe.Message, xe.HelpLink, xe.Source);
521                 else
522                     LoggingHelper.LogError(null, null, null, xamlFilePath, 0, 0, 0, 0, e.Message, e.HelpLink, e.Source);
523
524                 if (null != e.StackTrace)
525                 {
526                     LoggingHelper.LogError(e.StackTrace);
527                 }
528
529                 return false;
530             }
531             else
532             {
533                 var examlDir = outputRootPath + @"res/examl/";
534                 if (Directory.Exists(examlDir))
535                 {
536                     Directory.CreateDirectory(examlDir);
537                 }
538
539                 var examlFilePath = examlDir + typeDef.FullName + ".examl";
540
541                 EXamlOperation.WriteOpertions(examlFilePath, visitorContext);
542             }
543
544             return true;
545         }
546
547         bool GenerateEXaml(string xamlFilePath, ModuleDefinition module, out IList<Exception> thrownExceptions)
548         {
549             thrownExceptions = null;
550
551             LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Parsing Xaml");
552             Stream xamlStream = File.Open(xamlFilePath, FileMode.Open);
553
554             string className;
555             if (!CecilExtensions.IsXaml(xamlStream, module, out className))
556             {
557                 thrownExceptions.Add(new Exception($"{xamlFilePath} is not xaml format file"));
558             }
559
560             xamlStream.Seek(0, SeekOrigin.Begin);
561             var typeDef = module.GetTypeDefinition(className);
562
563             if (null == typeDef)
564             {
565                 throw new Exception($"Can't find type \"{className}\" in assembly \"{module.Assembly.FullName}\"");
566             }
567
568             var rootnode = ParseXaml(xamlStream, typeDef);
569
570             xamlStream.Close();
571
572             if (rootnode == null)
573             {
574                 LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}failed.");
575                 return false;
576             }
577             LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}done.");
578
579             hasCompiledXamlResources = true;
580
581             LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Replacing {0}.InitializeComponent ()");
582             Exception e;
583
584             var visitorContext = new EXamlContext(typeDef, module, null);
585
586             if (!TryCoreCompile(rootnode, visitorContext, out e))
587             {
588                 LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}failed.");
589                 (thrownExceptions = thrownExceptions ?? new List<Exception>()).Add(e);
590                 if (e is XamlParseException xpe)
591                     LoggingHelper.LogError(null, null, null, xamlFilePath, xpe.XmlInfo.LineNumber, xpe.XmlInfo.LinePosition, 0, 0, xpe.Message, xpe.HelpLink, xpe.Source);
592                 else if (e is XmlException xe)
593                     LoggingHelper.LogError(null, null, null, xamlFilePath, xe.LineNumber, xe.LinePosition, 0, 0, xe.Message, xe.HelpLink, xe.Source);
594                 else
595                     LoggingHelper.LogError(null, null, null, xamlFilePath, 0, 0, 0, 0, e.Message, e.HelpLink, e.Source);
596
597                 if (null != e.StackTrace)
598                 {
599                     LoggingHelper.LogMessage(Low, e.StackTrace);
600                 }
601
602                 return false;
603             }
604             else
605             {
606                 var examlDir = outputRootPath + @"res/examl/";
607                 if (Directory.Exists(examlDir))
608                 {
609                     Directory.CreateDirectory(examlDir);
610                 }
611
612                 var examlFilePath = examlDir + typeDef.FullName + ".examl";
613
614                 EXamlOperation.WriteOpertions(examlFilePath, visitorContext);
615             }
616
617             return true;
618         }
619
620
621         bool InjectionMethodGetEXamlPath(TypeDefinition typeDef)
622         {
623             var getEXamlPathComp = typeDef.Methods.FirstOrDefault(md => md.Name == "GetEXamlPath");
624             if (getEXamlPathComp == null)
625             {
626                 LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}no GetEXamlPath found... skipped.");
627                 return false;
628             }
629
630             var examlRelativePath = @"examl/" + typeDef.FullName + ".examl";
631             getEXamlPathComp.Body.Instructions.Clear();
632             getEXamlPathComp.Body.GetILProcessor().Emit(OpCodes.Ldstr, examlRelativePath);
633             getEXamlPathComp.Body.GetILProcessor().Emit(OpCodes.Ret);
634
635             return true;
636         }
637
638         bool TryCoreCompile(MethodDefinition initComp, ILRootNode rootnode, string embeddedResourceNameSpace, out Exception exception)
639         {
640             try
641             {
642                 var body = new MethodBody(initComp);
643                 var module = body.Method.Module;
644                 var type = initComp.DeclaringType;
645
646                 MethodDefinition constructorOfRemoveEventsType;
647                 TypeDefinition typeOfRemoveEvents = CreateTypeForRemoveEvents(type, out constructorOfRemoveEventsType);
648
649                 var field = type.GetOrCreateField("___Info_Of_RemoveEvent___", FieldAttributes.Private, typeOfRemoveEvents);
650
651                 body.InitLocals = true;
652                 var il = body.GetILProcessor();
653                 il.Emit(OpCodes.Ldarg_0);
654                 il.Emit(OpCodes.Newobj, constructorOfRemoveEventsType);
655                 il.Emit(OpCodes.Stfld, field);
656
657                 var resourcePath = GetPathForType(module, type);
658
659                 il.Emit(Nop);
660
661                 List<Instruction> insOfAddEvent = new List<Instruction>();
662
663                 var visitorContext = new ILContext(il, body, insOfAddEvent, module, embeddedResourceNameSpace);
664
665                 rootnode.Accept(new XamlNodeVisitor((node, parent) => node.Parent = parent), null);
666                 rootnode.Accept(new ExpandMarkupsVisitor(visitorContext), null);
667                 rootnode.Accept(new PruneIgnoredNodesVisitor(), null);
668                 rootnode.Accept(new CreateObjectVisitor(visitorContext), null);
669
670                 Set(visitorContext, visitorContext.Variables[rootnode], "IsCreateByXaml", new ValueNode("true", rootnode.NamespaceResolver), null);
671
672                 rootnode.Accept(new SetNamescopesAndRegisterNamesVisitor(visitorContext), null);
673                 rootnode.Accept(new SetFieldVisitor(visitorContext), null);
674                 rootnode.Accept(new SetResourcesVisitor(visitorContext), null);
675                 rootnode.Accept(new SetPropertiesVisitor(visitorContext, true), null);
676
677                 AddInsOfRemoveEvent(il, visitorContext.InsOfAddEvent, typeOfRemoveEvents);
678
679                 il.Emit(Ret);
680                 initComp.Body = body;
681                 exception = null;
682                 return true;
683             }
684             catch (Exception e)
685             {
686                 XamlParseException xamlParseException = e as XamlParseException;
687                 if (null != xamlParseException)
688                 {
689                     XamlParseException ret = new XamlParseException(xamlParseException.Message + "\n" + ReferencePath, xamlParseException.XmlInfo, xamlParseException.InnerException);
690                     exception = ret;
691                 }
692                 else
693                 {
694                     exception = e;
695                 }
696
697                 return false;
698             }
699         }
700
701         private void AddInsOfRemoveEvent(ILProcessor ilOfInit, List<Instruction> instructions, TypeDefinition typeDef)
702         {
703             MethodDefinition methodCall = typeDef.GetOrCreateMethod("Call", MethodAttributes.Public, typeof(void));
704             methodCall.Body.Instructions.Clear();
705
706             var fieldOfRemoveEvent = typeDef.DeclaringType.Fields.FirstOrDefault(a => a.Name == "___Info_Of_RemoveEvent___");
707
708             var il = methodCall.Body.GetILProcessor();
709
710             foreach (var ins in instructions)
711             {
712                 if (ins.OpCode == OpCodes.Ldloc
713                     &&
714                     ins.Operand is VariableDefinition variable)
715                 {
716                     var fieldName = "field" + variable.Index;
717                     var field = typeDef.GetOrCreateField(fieldName, FieldAttributes.Public, variable.VariableType);
718
719                     ilOfInit.Emit(OpCodes.Ldarg_0);
720                     ilOfInit.Emit(OpCodes.Ldfld, fieldOfRemoveEvent);
721                     ilOfInit.Emit(OpCodes.Ldloc, variable);
722                     ilOfInit.Emit(OpCodes.Stfld, field);
723
724                     methodCall.Body.Instructions.Add(Instruction.Create(Ldarg_0));
725                     methodCall.Body.Instructions.Add(Instruction.Create(Ldfld, field));
726                 }
727                 else
728                 {
729                     bool isReplaced = false;
730                     if (ins.OpCode == OpCodes.Callvirt && ins.Operand is MethodReference method)
731                     {
732                         if (method.Name.StartsWith("add_"))
733                         {
734                             var eventName = method.Name.Substring("add_".Length);
735                             TypeReference _;
736                             var typeOfEvent = method.DeclaringType.GetEvent(a => a.Name == eventName, out _);
737
738                             if (typeOfEvent is EventDefinition)
739                             {
740                                 var methodOfRemoveEvent = typeDef.Module.ImportReference(method.DeclaringType.ResolveCached()?.Methods.FirstOrDefault(a => a.Name == "remove_" + eventName));
741                                 if (null != methodOfRemoveEvent)
742                                 {
743                                     var newIns = Instruction.Create(ins.OpCode, methodOfRemoveEvent);
744                                     methodCall.Body.Instructions.Add(newIns);
745
746                                     isReplaced = true;
747                                 }
748                             }
749                         }
750                     }
751
752                     if (false == isReplaced)
753                     {
754                         methodCall.Body.Instructions.Add(ins);
755                     }
756                 }
757             }
758
759             methodCall.Body.Instructions.Add(Instruction.Create(Ret));
760
761             var removeEventMethod = typeDef.DeclaringType.Methods.FirstOrDefault(a => a.Name == "RemoveEventsInXaml");
762             if (null != removeEventMethod)
763             {
764                 removeEventMethod.Body.Instructions.Clear();
765                 var ilRemoveEvent = removeEventMethod.Body.GetILProcessor();
766
767                 ilRemoveEvent.Emit(Ldarg_0);
768                 ilRemoveEvent.Emit(Ldfld, fieldOfRemoveEvent);
769                 ilRemoveEvent.Emit(Dup);
770
771                 var insOfCall = Instruction.Create(Call, methodCall.Resolve());
772
773                 ilRemoveEvent.Emit(Brtrue_S, insOfCall);
774                 ilRemoveEvent.Emit(Pop);
775
776                 var endIns = Instruction.Create(Ret);
777
778                 ilRemoveEvent.Emit(Br_S, endIns);
779                 ilRemoveEvent.Append(insOfCall);
780
781                 ilRemoveEvent.Append(endIns);
782             }
783         }
784
785         TypeDefinition CreateTypeForRemoveEvents(TypeDefinition typeDef, out MethodDefinition constructor)
786         {
787             var module = typeDef.Module;
788
789             var name = "___Type___For___RemoveEvent___";
790             var nestType = typeDef.NestedTypes.FirstOrDefault(a => a.Name == name);
791
792             if (null == nestType)
793             {
794                 nestType = new TypeDefinition(typeDef.Namespace, name, TypeAttributes.NestedPrivate | TypeAttributes.BeforeFieldInit | TypeAttributes.Sealed | TypeAttributes.AnsiClass);
795                 nestType.BaseType = module.ImportReference(typeof(object));
796                 typeDef.NestedTypes.Add(nestType);
797
798                 constructor = nestType.AddDefaultConstructor();
799             }
800             else
801             {
802                 constructor = nestType.Methods.FirstOrDefault(a => a.IsConstructor);
803             }
804
805             return nestType;
806         }
807
808         bool TryCoreCompile(ILRootNode rootnode, EXamlContext visitorContext, out Exception exception)
809         {
810             try
811             {
812                 XmlTypeExtensions.s_xmlnsDefinitions?.Clear();
813                 XmlTypeExtensions.s_xmlnsDefinitions = null;
814
815                 visitorContext.Values[rootnode] = new EXamlCreateObject(visitorContext, null, rootnode.TypeReference);
816
817                 rootnode.Accept(new XamlNodeVisitor((node, parent) => node.Parent = parent), null);
818                 rootnode.Accept(new EXamlExpandMarkupsVisitor(visitorContext), null);
819                 rootnode.Accept(new PruneIgnoredNodesVisitor(), null);
820                 rootnode.Accept(new EXamlCreateObjectVisitor(visitorContext), null);
821                 rootnode.Accept(new EXamlSetNamescopesAndRegisterNamesVisitor(visitorContext), null);
822                 rootnode.Accept(new EXamlSetFieldVisitor(visitorContext), null);
823                 rootnode.Accept(new EXamlSetResourcesVisitor(visitorContext), null);
824                 rootnode.Accept(new EXamlSetPropertiesVisitor(visitorContext, true), null);
825
826                 exception = null;
827                 return true;
828             }
829             catch (Exception e)
830             {
831                 XamlParseException xamlParseException = e as XamlParseException;
832                 if (null != xamlParseException)
833                 {
834                     XamlParseException ret = new XamlParseException(xamlParseException.Message + "\n" + ReferencePath, xamlParseException.XmlInfo, xamlParseException.InnerException);
835                     exception = ret;
836                 }
837                 else
838                 {
839                     exception = e;
840                 }
841
842                 return false;
843             }
844         }
845
846         private void Set(ILContext Context, VariableDefinition parent, string localName, INode node, IXmlLineInfo iXmlLineInfo)
847         {
848             var module = Context.Body.Method.Module;
849             TypeReference declaringTypeReference;
850             var property = parent.VariableType.GetProperty(pd => pd.Name == localName, out declaringTypeReference);
851             if (null == property)
852             {
853                 return;
854             }
855             var propertySetter = property.SetMethod;
856
857             module.ImportReference(parent.VariableType.ResolveCached());
858             var propertySetterRef = module.ImportReference(module.ImportReference(propertySetter).ResolveGenericParameters(declaringTypeReference, module));
859             propertySetterRef.ImportTypes(module);
860             var propertyType = property.ResolveGenericPropertyType(declaringTypeReference, module);
861             var valueNode = node as ValueNode;
862             var elementNode = node as IElementNode;
863
864             if (parent.VariableType.IsValueType)
865                 Context.IL.Emit(OpCodes.Ldloca, parent);
866             else
867                 Context.IL.Emit(OpCodes.Ldloc, parent);
868
869             if (valueNode != null)
870             {
871                 foreach (var instruction in valueNode.PushConvertedValue(Context, propertyType, new ICustomAttributeProvider[] { property, propertyType.ResolveCached() }, valueNode.PushServiceProvider(Context, propertyRef: property), false, true))
872                 {
873                     Context.IL.Append(instruction);
874                 }
875
876                 if (parent.VariableType.IsValueType)
877                     Context.IL.Emit(OpCodes.Call, propertySetterRef);
878                 else
879                     Context.IL.Emit(OpCodes.Callvirt, propertySetterRef);
880             }
881         }
882
883         internal static string GetPathForType(ModuleDefinition module, TypeReference type)
884         {
885             foreach (var ca in type.Module.GetCustomAttributes())
886             {
887                 if (!TypeRefComparer.Default.Equals(ca.AttributeType, module.ImportReference((xamlAssemblyName, xamlNameSpace, "XamlResourceIdAttribute"))))
888                     continue;
889                 if (!TypeRefComparer.Default.Equals(ca.ConstructorArguments[2].Value as TypeReference, type))
890                     continue;
891                 return ca.ConstructorArguments[1].Value as string;
892             }
893             return null;
894         }
895
896         internal static string GetResourceIdForPath(ModuleDefinition module, string path)
897         {
898             foreach (var ca in module.GetCustomAttributes())
899             {
900                 if (!TypeRefComparer.Default.Equals(ca.AttributeType, module.ImportReference((xamlAssemblyName, xamlNameSpace, "XamlResourceIdAttribute"))))
901                     continue;
902                 if (ca.ConstructorArguments[1].Value as string != path)
903                     continue;
904                 return ca.ConstructorArguments[0].Value as string;
905             }
906             return null;
907         }
908     }
909 }
910