Release 9.0.0.16873
[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         bool DoInjection(TypeDefinition typeDef, EmbeddedResource resource, out IList<Exception> thrownExceptions)
384         {
385             thrownExceptions = null;
386
387             var initComp = typeDef.Methods.FirstOrDefault(md => md.Name == "InitializeComponent");
388             if (initComp == null)
389             {
390                 LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}no InitializeComponent found... skipped.");
391                 return false;
392             }
393
394             CustomAttribute xamlFilePathAttr;
395             var xamlFilePath = typeDef.HasCustomAttributes && (xamlFilePathAttr = typeDef.CustomAttributes.FirstOrDefault(ca => ca.AttributeType.FullName == "Tizen.NUI.Xaml.XamlFilePathAttribute")) != null ?
396                                       (string)xamlFilePathAttr.ConstructorArguments[0].Value :
397                                       resource.Name;
398
399             var initCompRuntime = typeDef.Methods.FirstOrDefault(md => md.Name == "__InitComponentRuntime");
400             if (initCompRuntime != null)
401                 LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}__InitComponentRuntime already exists... not creating");
402             else
403             {
404                 LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Creating empty {typeDef.Name}.__InitComponentRuntime");
405                 initCompRuntime = new MethodDefinition("__InitComponentRuntime", initComp.Attributes, initComp.ReturnType);
406                 initCompRuntime.Body.InitLocals = true;
407                 LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}done.");
408                 LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Copying body of InitializeComponent to __InitComponentRuntime");
409                 initCompRuntime.Body = new MethodBody(initCompRuntime);
410                 var iCRIl = initCompRuntime.Body.GetILProcessor();
411                 foreach (var instr in initComp.Body.Instructions)
412                     iCRIl.Append(instr);
413                 initComp.Body.Instructions.Clear();
414                 initComp.Body.GetILProcessor().Emit(OpCodes.Ret);
415                 initComp.Body.InitLocals = true;
416
417                 typeDef.Methods.Add(initCompRuntime);
418                 LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}done.");
419             }
420
421             LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Parsing Xaml");
422             var rootnode = ParseXaml(resource.GetResourceStream(), typeDef);
423             if (rootnode == null)
424             {
425                 LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}failed.");
426                 return false;
427             }
428             LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}done.");
429
430             hasCompiledXamlResources = true;
431
432             LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Replacing {0}.InitializeComponent ()");
433             Exception e;
434             if (!TryCoreCompile(initComp, rootnode, out e))
435             {
436                 LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}failed.");
437                 (thrownExceptions = thrownExceptions ?? new List<Exception>()).Add(e);
438                 if (e is XamlParseException xpe)
439                     LoggingHelper.LogError(null, null, null, xamlFilePath, xpe.XmlInfo.LineNumber, xpe.XmlInfo.LinePosition, 0, 0, xpe.Message, xpe.HelpLink, xpe.Source);
440                 else if (e is XmlException xe)
441                     LoggingHelper.LogError(null, null, null, xamlFilePath, xe.LineNumber, xe.LinePosition, 0, 0, xe.Message, xe.HelpLink, xe.Source);
442                 else
443                     LoggingHelper.LogError(null, null, null, xamlFilePath, 0, 0, 0, 0, e.Message, e.HelpLink, e.Source);
444
445                 if (null != e.StackTrace)
446                 {
447                     LoggingHelper.LogMessage(Low, e.StackTrace);
448                 }
449
450                 return false;
451             }
452             if (Type != null)
453                 InitCompForType = initComp;
454
455             LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}done.");
456
457             if (OptimizeIL)
458             {
459                 LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Optimizing IL");
460                 initComp.Body.Optimize();
461                 LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}done.");
462             }
463
464 #pragma warning disable 0618
465             if (OutputGeneratedILAsCode)
466                 LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Decompiling option has been removed. Use a 3rd party decompiler to admire the beauty of the IL generated");
467 #pragma warning restore 0618
468
469             return true;
470         }
471
472         bool GenerateEXaml(TypeDefinition typeDef, EmbeddedResource resource, out IList<Exception> thrownExceptions)
473         {
474             thrownExceptions = null;
475
476             ModuleDefinition module = typeDef.Module;
477
478             CustomAttribute xamlFilePathAttr;
479             var xamlFilePath = typeDef.HasCustomAttributes && (xamlFilePathAttr = typeDef.CustomAttributes.FirstOrDefault(ca => ca.AttributeType.FullName == "Tizen.NUI.Xaml.XamlFilePathAttribute")) != null ?
480                                       (string)xamlFilePathAttr.ConstructorArguments[0].Value :
481                                       resource.Name;
482
483             LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Parsing Xaml");
484             var rootnode = ParseXaml(resource.GetResourceStream(), typeDef);
485             if (rootnode == null)
486             {
487                 LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}failed.");
488                 return false;
489             }
490             LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}done.");
491
492             hasCompiledXamlResources = true;
493
494             LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Replacing {0}.InitializeComponent ()");
495             Exception e;
496
497             var visitorContext = new EXamlContext(typeDef, typeDef.Module);
498
499             if (!TryCoreCompile(rootnode, visitorContext, out e))
500             {
501                 LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}failed.");
502                 (thrownExceptions = thrownExceptions ?? new List<Exception>()).Add(e);
503                 if (e is XamlParseException xpe)
504                     LoggingHelper.LogError(null, null, null, xamlFilePath, xpe.XmlInfo.LineNumber, xpe.XmlInfo.LinePosition, 0, 0, xpe.Message, xpe.HelpLink, xpe.Source);
505                 else if (e is XmlException xe)
506                     LoggingHelper.LogError(null, null, null, xamlFilePath, xe.LineNumber, xe.LinePosition, 0, 0, xe.Message, xe.HelpLink, xe.Source);
507                 else
508                     LoggingHelper.LogError(null, null, null, xamlFilePath, 0, 0, 0, 0, e.Message, e.HelpLink, e.Source);
509
510                 if (null != e.StackTrace)
511                 {
512                     LoggingHelper.LogError(e.StackTrace);
513                 }
514
515                 return false;
516             }
517             else
518             {
519                 var examlDir = outputRootPath + @"res/examl/";
520                 if (Directory.Exists(examlDir))
521                 {
522                     Directory.CreateDirectory(examlDir);
523                 }
524
525                 var examlFilePath = examlDir + typeDef.FullName + ".examl";
526
527                 EXamlOperation.WriteOpertions(examlFilePath, visitorContext);
528             }
529
530             return true;
531         }
532
533         bool GenerateEXaml(string xamlFilePath, ModuleDefinition module, out IList<Exception> thrownExceptions)
534         {
535             thrownExceptions = null;
536
537             LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Parsing Xaml");
538             Stream xamlStream = File.Open(xamlFilePath, FileMode.Open);
539
540             string className;
541             if (!CecilExtensions.IsXaml(xamlStream, module, out className))
542             {
543                 thrownExceptions.Add(new Exception($"{xamlFilePath} is not xaml format file"));
544             }
545
546             xamlStream.Seek(0, SeekOrigin.Begin);
547             var typeDef = module.GetTypeDefinition(className);
548
549             if (null == typeDef)
550             {
551                 throw new Exception($"Can't find type \"{className}\" in assembly \"{module.Assembly.FullName}\"");
552             }
553
554             var rootnode = ParseXaml(xamlStream, typeDef);
555
556             xamlStream.Close();
557
558             if (rootnode == null)
559             {
560                 LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}failed.");
561                 return false;
562             }
563             LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}done.");
564
565             hasCompiledXamlResources = true;
566
567             LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Replacing {0}.InitializeComponent ()");
568             Exception e;
569
570             var visitorContext = new EXamlContext(typeDef, module);
571
572             if (!TryCoreCompile(rootnode, visitorContext, out e))
573             {
574                 LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}failed.");
575                 (thrownExceptions = thrownExceptions ?? new List<Exception>()).Add(e);
576                 if (e is XamlParseException xpe)
577                     LoggingHelper.LogError(null, null, null, xamlFilePath, xpe.XmlInfo.LineNumber, xpe.XmlInfo.LinePosition, 0, 0, xpe.Message, xpe.HelpLink, xpe.Source);
578                 else if (e is XmlException xe)
579                     LoggingHelper.LogError(null, null, null, xamlFilePath, xe.LineNumber, xe.LinePosition, 0, 0, xe.Message, xe.HelpLink, xe.Source);
580                 else
581                     LoggingHelper.LogError(null, null, null, xamlFilePath, 0, 0, 0, 0, e.Message, e.HelpLink, e.Source);
582
583                 if (null != e.StackTrace)
584                 {
585                     LoggingHelper.LogMessage(Low, e.StackTrace);
586                 }
587
588                 return false;
589             }
590             else
591             {
592                 var examlDir = outputRootPath + @"res/examl/";
593                 if (Directory.Exists(examlDir))
594                 {
595                     Directory.CreateDirectory(examlDir);
596                 }
597
598                 var examlFilePath = examlDir + typeDef.FullName + ".examl";
599
600                 EXamlOperation.WriteOpertions(examlFilePath, visitorContext);
601             }
602
603             return true;
604         }
605
606
607         bool InjectionMethodGetEXamlPath(TypeDefinition typeDef)
608         {
609             var getEXamlPathComp = typeDef.Methods.FirstOrDefault(md => md.Name == "GetEXamlPath");
610             if (getEXamlPathComp == null)
611             {
612                 LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}no GetEXamlPath found... skipped.");
613                 return false;
614             }
615
616             var examlRelativePath = @"examl/" + typeDef.FullName + ".examl";
617             getEXamlPathComp.Body.Instructions.Clear();
618             getEXamlPathComp.Body.GetILProcessor().Emit(OpCodes.Ldstr, examlRelativePath);
619             getEXamlPathComp.Body.GetILProcessor().Emit(OpCodes.Ret);
620
621             return true;
622         }
623
624         bool TryCoreCompile(MethodDefinition initComp, ILRootNode rootnode, out Exception exception)
625         {
626             try
627             {
628                 var body = new MethodBody(initComp);
629                 var module = body.Method.Module;
630                 var type = initComp.DeclaringType;
631
632                 MethodDefinition constructorOfRemoveEventsType;
633                 TypeDefinition typeOfRemoveEvents = CreateTypeForRemoveEvents(type, out constructorOfRemoveEventsType);
634
635                 var field = type.GetOrCreateField("___Info_Of_RemoveEvent___", FieldAttributes.Private, typeOfRemoveEvents);
636
637                 body.InitLocals = true;
638                 var il = body.GetILProcessor();
639                 il.Emit(OpCodes.Ldarg_0);
640                 il.Emit(OpCodes.Newobj, constructorOfRemoveEventsType);
641                 il.Emit(OpCodes.Stfld, field);
642
643                 var resourcePath = GetPathForType(module, type);
644
645                 il.Emit(Nop);
646
647                 List<Instruction> insOfAddEvent = new List<Instruction>();
648
649                 var visitorContext = new ILContext(il, body, insOfAddEvent, module);
650
651                 rootnode.Accept(new XamlNodeVisitor((node, parent) => node.Parent = parent), null);
652                 rootnode.Accept(new ExpandMarkupsVisitor(visitorContext), null);
653                 rootnode.Accept(new PruneIgnoredNodesVisitor(), null);
654                 rootnode.Accept(new CreateObjectVisitor(visitorContext), null);
655
656                 Set(visitorContext, visitorContext.Variables[rootnode], "IsCreateByXaml", new ValueNode("true", rootnode.NamespaceResolver), null);
657
658                 rootnode.Accept(new SetNamescopesAndRegisterNamesVisitor(visitorContext), null);
659                 rootnode.Accept(new SetFieldVisitor(visitorContext), null);
660                 rootnode.Accept(new SetResourcesVisitor(visitorContext), null);
661                 rootnode.Accept(new SetPropertiesVisitor(visitorContext, true), null);
662
663                 AddInsOfRemoveEvent(il, visitorContext.InsOfAddEvent, typeOfRemoveEvents);
664
665                 il.Emit(Ret);
666                 initComp.Body = body;
667                 exception = null;
668                 return true;
669             }
670             catch (Exception e)
671             {
672                 XamlParseException xamlParseException = e as XamlParseException;
673                 if (null != xamlParseException)
674                 {
675                     XamlParseException ret = new XamlParseException(xamlParseException.Message + "\n" + ReferencePath, xamlParseException.XmlInfo, xamlParseException.InnerException);
676                     exception = ret;
677                 }
678                 else
679                 {
680                     exception = e;
681                 }
682
683                 return false;
684             }
685         }
686
687         private void AddInsOfRemoveEvent(ILProcessor ilOfInit, List<Instruction> instructions, TypeDefinition typeDef)
688         {
689             MethodDefinition methodCall = typeDef.GetOrCreateMethod("Call", MethodAttributes.Public, typeof(void));
690             methodCall.Body.Instructions.Clear();
691
692             var fieldOfRemoveEvent = typeDef.DeclaringType.Fields.FirstOrDefault(a => a.Name == "___Info_Of_RemoveEvent___");
693
694             var il = methodCall.Body.GetILProcessor();
695
696             foreach (var ins in instructions)
697             {
698                 if (ins.OpCode == OpCodes.Ldloc
699                     &&
700                     ins.Operand is VariableDefinition variable)
701                 {
702                     var fieldName = "field" + variable.Index;
703                     var field = typeDef.GetOrCreateField(fieldName, FieldAttributes.Public, variable.VariableType);
704
705                     ilOfInit.Emit(OpCodes.Ldarg_0);
706                     ilOfInit.Emit(OpCodes.Ldfld, fieldOfRemoveEvent);
707                     ilOfInit.Emit(OpCodes.Ldloc, variable);
708                     ilOfInit.Emit(OpCodes.Stfld, field);
709
710                     methodCall.Body.Instructions.Add(Instruction.Create(Ldarg_0));
711                     methodCall.Body.Instructions.Add(Instruction.Create(Ldfld, field));
712                 }
713                 else
714                 {
715                     bool isReplaced = false;
716                     if (ins.OpCode == OpCodes.Callvirt && ins.Operand is MethodReference method)
717                     {
718                         if (method.Name.StartsWith("add_"))
719                         {
720                             var eventName = method.Name.Substring("add_".Length);
721                             TypeReference _;
722                             var typeOfEvent = method.DeclaringType.GetEvent(a => a.Name == eventName, out _);
723
724                             if (typeOfEvent is EventDefinition)
725                             {
726                                 var methodOfRemoveEvent = typeDef.Module.ImportReference(method.DeclaringType.ResolveCached()?.Methods.FirstOrDefault(a => a.Name == "remove_" + eventName));
727                                 if (null != methodOfRemoveEvent)
728                                 {
729                                     var newIns = Instruction.Create(ins.OpCode, methodOfRemoveEvent);
730                                     methodCall.Body.Instructions.Add(newIns);
731
732                                     isReplaced = true;
733                                 }
734                             }
735                         }
736                     }
737
738                     if (false == isReplaced)
739                     {
740                         methodCall.Body.Instructions.Add(ins);
741                     }
742                 }
743             }
744
745             methodCall.Body.Instructions.Add(Instruction.Create(Ret));
746
747             var removeEventMethod = typeDef.DeclaringType.Methods.FirstOrDefault(a => a.Name == "RemoveEventsInXaml");
748             if (null != removeEventMethod)
749             {
750                 removeEventMethod.Body.Instructions.Clear();
751                 var ilRemoveEvent = removeEventMethod.Body.GetILProcessor();
752
753                 ilRemoveEvent.Emit(Ldarg_0);
754                 ilRemoveEvent.Emit(Ldfld, fieldOfRemoveEvent);
755                 ilRemoveEvent.Emit(Dup);
756
757                 var insOfCall = Instruction.Create(Call, methodCall.Resolve());
758
759                 ilRemoveEvent.Emit(Brtrue_S, insOfCall);
760                 ilRemoveEvent.Emit(Pop);
761
762                 var endIns = Instruction.Create(Ret);
763
764                 ilRemoveEvent.Emit(Br_S, endIns);
765                 ilRemoveEvent.Append(insOfCall);
766
767                 ilRemoveEvent.Append(endIns);
768             }
769         }
770
771         TypeDefinition CreateTypeForRemoveEvents(TypeDefinition typeDef, out MethodDefinition constructor)
772         {
773             var module = typeDef.Module;
774
775             var name = "___Type___For___RemoveEvent___";
776             var nestType = typeDef.NestedTypes.FirstOrDefault(a => a.Name == name);
777
778             if (null == nestType)
779             {
780                 nestType = new TypeDefinition(typeDef.Namespace, name, TypeAttributes.NestedPrivate | TypeAttributes.BeforeFieldInit | TypeAttributes.Sealed | TypeAttributes.AnsiClass);
781                 nestType.BaseType = module.ImportReference(typeof(object));
782                 typeDef.NestedTypes.Add(nestType);
783
784                 constructor = nestType.AddDefaultConstructor();
785             }
786             else
787             {
788                 constructor = nestType.Methods.FirstOrDefault(a => a.IsConstructor);
789             }
790
791             return nestType;
792         }
793
794         bool TryCoreCompile(ILRootNode rootnode, EXamlContext visitorContext, out Exception exception)
795         {
796             try
797             {
798                 XmlTypeExtensions.s_xmlnsDefinitions?.Clear();
799                 XmlTypeExtensions.s_xmlnsDefinitions = null;
800
801                 visitorContext.Values[rootnode] = new EXamlCreateObject(visitorContext, null, rootnode.TypeReference);
802
803                 rootnode.Accept(new XamlNodeVisitor((node, parent) => node.Parent = parent), null);
804                 rootnode.Accept(new EXamlExpandMarkupsVisitor(visitorContext), null);
805                 rootnode.Accept(new PruneIgnoredNodesVisitor(), null);
806                 rootnode.Accept(new EXamlCreateObjectVisitor(visitorContext), null);
807                 rootnode.Accept(new EXamlSetNamescopesAndRegisterNamesVisitor(visitorContext), null);
808                 rootnode.Accept(new EXamlSetFieldVisitor(visitorContext), null);
809                 rootnode.Accept(new EXamlSetResourcesVisitor(visitorContext), null);
810                 rootnode.Accept(new EXamlSetPropertiesVisitor(visitorContext, true), null);
811
812                 exception = null;
813                 return true;
814             }
815             catch (Exception e)
816             {
817                 XamlParseException xamlParseException = e as XamlParseException;
818                 if (null != xamlParseException)
819                 {
820                     XamlParseException ret = new XamlParseException(xamlParseException.Message + "\n" + ReferencePath, xamlParseException.XmlInfo, xamlParseException.InnerException);
821                     exception = ret;
822                 }
823                 else
824                 {
825                     exception = e;
826                 }
827
828                 return false;
829             }
830         }
831
832         private void Set(ILContext Context, VariableDefinition parent, string localName, INode node, IXmlLineInfo iXmlLineInfo)
833         {
834             var module = Context.Body.Method.Module;
835             TypeReference declaringTypeReference;
836             var property = parent.VariableType.GetProperty(pd => pd.Name == localName, out declaringTypeReference);
837             if (null == property)
838             {
839                 return;
840             }
841             var propertySetter = property.SetMethod;
842
843             module.ImportReference(parent.VariableType.ResolveCached());
844             var propertySetterRef = module.ImportReference(module.ImportReference(propertySetter).ResolveGenericParameters(declaringTypeReference, module));
845             propertySetterRef.ImportTypes(module);
846             var propertyType = property.ResolveGenericPropertyType(declaringTypeReference, module);
847             var valueNode = node as ValueNode;
848             var elementNode = node as IElementNode;
849
850             if (parent.VariableType.IsValueType)
851                 Context.IL.Emit(OpCodes.Ldloca, parent);
852             else
853                 Context.IL.Emit(OpCodes.Ldloc, parent);
854
855             if (valueNode != null)
856             {
857                 foreach (var instruction in valueNode.PushConvertedValue(Context, propertyType, new ICustomAttributeProvider[] { property, propertyType.ResolveCached() }, valueNode.PushServiceProvider(Context, propertyRef: property), false, true))
858                 {
859                     Context.IL.Append(instruction);
860                 }
861
862                 if (parent.VariableType.IsValueType)
863                     Context.IL.Emit(OpCodes.Call, propertySetterRef);
864                 else
865                     Context.IL.Emit(OpCodes.Callvirt, propertySetterRef);
866             }
867         }
868
869         internal static string GetPathForType(ModuleDefinition module, TypeReference type)
870         {
871             foreach (var ca in type.Module.GetCustomAttributes())
872             {
873                 if (!TypeRefComparer.Default.Equals(ca.AttributeType, module.ImportReference((xamlAssemblyName, xamlNameSpace, "XamlResourceIdAttribute"))))
874                     continue;
875                 if (!TypeRefComparer.Default.Equals(ca.ConstructorArguments[2].Value as TypeReference, type))
876                     continue;
877                 return ca.ConstructorArguments[1].Value as string;
878             }
879             return null;
880         }
881
882         internal static string GetResourceIdForPath(ModuleDefinition module, string path)
883         {
884             foreach (var ca in module.GetCustomAttributes())
885             {
886                 if (!TypeRefComparer.Default.Equals(ca.AttributeType, module.ImportReference((xamlAssemblyName, xamlNameSpace, "XamlResourceIdAttribute"))))
887                     continue;
888                 if (ca.ConstructorArguments[1].Value as string != path)
889                     continue;
890                 return ca.ConstructorArguments[0].Value as string;
891             }
892             return null;
893         }
894     }
895 }
896