AvaloniaXamlIlRuntimeCompiler.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Globalization;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Linq.Expressions;
  7. using System.Reflection;
  8. using System.Reflection.Emit;
  9. using System.Runtime.InteropServices;
  10. using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions;
  11. using Avalonia.Markup.Xaml.XamlIl.Runtime;
  12. using Avalonia.Platform;
  13. using XamlX.Transform;
  14. using XamlX.TypeSystem;
  15. using XamlX.IL;
  16. using XamlX.Emit;
  17. #if RUNTIME_XAML_CECIL
  18. using TypeAttributes = Mono.Cecil.TypeAttributes;
  19. using Mono.Cecil;
  20. using XamlX.Ast;
  21. using XamlX.IL.Cecil;
  22. #endif
  23. namespace Avalonia.Markup.Xaml.XamlIl
  24. {
  25. static class AvaloniaXamlIlRuntimeCompiler
  26. {
  27. #if !RUNTIME_XAML_CECIL
  28. private static SreTypeSystem _sreTypeSystem;
  29. private static Type _ignoresAccessChecksFromAttribute;
  30. private static ModuleBuilder _sreBuilder;
  31. private static IXamlType _sreContextType;
  32. private static XamlLanguageTypeMappings _sreMappings;
  33. private static XamlLanguageEmitMappings<IXamlILEmitter, XamlILNodeEmitResult> _sreEmitMappings;
  34. private static XamlXmlnsMappings _sreXmlns;
  35. private static AssemblyBuilder _sreAsm;
  36. private static bool _sreCanSave;
  37. public static void DumpRuntimeCompilationResults()
  38. {
  39. if (_sreBuilder == null)
  40. return;
  41. var saveMethod = _sreAsm.GetType().GetMethods()
  42. .FirstOrDefault(m => m.Name == "Save" && m.GetParameters().Length == 1);
  43. if (saveMethod == null)
  44. return;
  45. try
  46. {
  47. _sreBuilder.CreateGlobalFunctions();
  48. saveMethod.Invoke(_sreAsm, new Object[] {"XamlIlLoader.ildump"});
  49. }
  50. catch
  51. {
  52. //Ignore
  53. }
  54. }
  55. static void InitializeSre()
  56. {
  57. if (_sreTypeSystem == null)
  58. _sreTypeSystem = new SreTypeSystem();
  59. if (_sreBuilder == null)
  60. {
  61. _sreCanSave = !(RuntimeInformation.FrameworkDescription.StartsWith(".NET Core"));
  62. var name = new AssemblyName(Guid.NewGuid().ToString("N"));
  63. if (_sreCanSave)
  64. {
  65. var define = AppDomain.CurrentDomain.GetType().GetMethods()
  66. .FirstOrDefault(m => m.Name == "DefineDynamicAssembly"
  67. && m.GetParameters().Length == 3 &&
  68. m.GetParameters()[2].ParameterType == typeof(string));
  69. if (define != null)
  70. _sreAsm = (AssemblyBuilder)define.Invoke(AppDomain.CurrentDomain, new object[]
  71. {
  72. name, (AssemblyBuilderAccess)3,
  73. Path.GetDirectoryName(typeof(AvaloniaXamlIlRuntimeCompiler).Assembly.GetModules()[0]
  74. .FullyQualifiedName)
  75. });
  76. else
  77. _sreCanSave = false;
  78. }
  79. if(_sreAsm == null)
  80. _sreAsm = AssemblyBuilder.DefineDynamicAssembly(name,
  81. AssemblyBuilderAccess.RunAndCollect);
  82. _sreBuilder = _sreAsm.DefineDynamicModule("XamlIlLoader.ildump");
  83. }
  84. if (_sreMappings == null)
  85. (_sreMappings, _sreEmitMappings) = AvaloniaXamlIlLanguage.Configure(_sreTypeSystem);
  86. if (_sreXmlns == null)
  87. _sreXmlns = XamlXmlnsMappings.Resolve(_sreTypeSystem, _sreMappings);
  88. if (_sreContextType == null)
  89. _sreContextType = XamlILContextDefinition.GenerateContextClass(
  90. _sreTypeSystem.CreateTypeBuilder(
  91. _sreBuilder.DefineType("XamlIlContext")), _sreTypeSystem, _sreMappings,
  92. _sreEmitMappings);
  93. if (_ignoresAccessChecksFromAttribute == null)
  94. _ignoresAccessChecksFromAttribute = EmitIgnoresAccessCheckAttributeDefinition(_sreBuilder);
  95. }
  96. static Type EmitIgnoresAccessCheckAttributeDefinition(ModuleBuilder builder)
  97. {
  98. var tb = builder.DefineType("System.Runtime.CompilerServices.IgnoresAccessChecksToAttribute",
  99. TypeAttributes.Class | TypeAttributes.Public, typeof(Attribute));
  100. var field = tb.DefineField("_name", typeof(string), FieldAttributes.Private);
  101. var propGet = tb.DefineMethod("get_AssemblyName", MethodAttributes.Public, typeof(string),
  102. Array.Empty<Type>());
  103. var propGetIl = propGet.GetILGenerator();
  104. propGetIl.Emit(OpCodes.Ldarg_0);
  105. propGetIl.Emit(OpCodes.Ldfld, field);
  106. propGetIl.Emit(OpCodes.Ret);
  107. var prop = tb.DefineProperty("AssemblyName", PropertyAttributes.None, typeof(string), Array.Empty<Type>());
  108. prop.SetGetMethod(propGet);
  109. var ctor = tb.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard,
  110. new[] { typeof(string) });
  111. var ctorIl = ctor.GetILGenerator();
  112. ctorIl.Emit(OpCodes.Ldarg_0);
  113. ctorIl.Emit(OpCodes.Ldarg_1);
  114. ctorIl.Emit(OpCodes.Stfld, field);
  115. ctorIl.Emit(OpCodes.Ldarg_0);
  116. ctorIl.Emit(OpCodes.Call, typeof(Attribute)
  117. .GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
  118. .First(x => x.GetParameters().Length == 0));
  119. ctorIl.Emit(OpCodes.Ret);
  120. tb.SetCustomAttribute(new CustomAttributeBuilder(
  121. typeof(AttributeUsageAttribute).GetConstructor(new[] { typeof(AttributeTargets) }),
  122. new object[] { AttributeTargets.Assembly },
  123. new[] { typeof(AttributeUsageAttribute).GetProperty("AllowMultiple") },
  124. new object[] { true }));
  125. return tb.CreateTypeInfo();
  126. }
  127. static void EmitIgnoresAccessCheckToAttribute(AssemblyName assemblyName)
  128. {
  129. var name = assemblyName.Name;
  130. var key = assemblyName.GetPublicKey();
  131. if (key != null)
  132. name += ", PublicKey=" + BitConverter.ToString(key).Replace("-", "").ToUpperInvariant();
  133. _sreAsm.SetCustomAttribute(new CustomAttributeBuilder(
  134. _ignoresAccessChecksFromAttribute.GetConstructors()[0],
  135. new object[] { name }));
  136. }
  137. static object LoadSre(string xaml, Assembly localAssembly, object rootInstance, Uri uri, bool isDesignMode)
  138. {
  139. var success = false;
  140. try
  141. {
  142. var rv = LoadSreCore(xaml, localAssembly, rootInstance, uri, isDesignMode);
  143. success = true;
  144. return rv;
  145. }
  146. finally
  147. {
  148. if(!success && _sreCanSave)
  149. DumpRuntimeCompilationResults();
  150. }
  151. }
  152. static object LoadSreCore(string xaml, Assembly localAssembly, object rootInstance, Uri uri, bool isDesignMode)
  153. {
  154. InitializeSre();
  155. if (localAssembly?.FullName != null)
  156. EmitIgnoresAccessCheckToAttribute(localAssembly.GetName());
  157. var asm = localAssembly == null ? null : _sreTypeSystem.GetAssembly(localAssembly);
  158. var tb = _sreBuilder.DefineType("Builder_" + Guid.NewGuid().ToString("N") + "_" + uri);
  159. var clrPropertyBuilder = tb.DefineNestedType("ClrProperties_" + Guid.NewGuid().ToString("N"));
  160. var indexerClosureType = _sreBuilder.DefineType("IndexerClosure_" + Guid.NewGuid().ToString("N"));
  161. var compiler = new AvaloniaXamlIlCompiler(new AvaloniaXamlIlCompilerConfiguration(_sreTypeSystem, asm,
  162. _sreMappings, _sreXmlns, AvaloniaXamlIlLanguage.CustomValueConverter,
  163. new XamlIlClrPropertyInfoEmitter(_sreTypeSystem.CreateTypeBuilder(clrPropertyBuilder)),
  164. new XamlIlPropertyInfoAccessorFactoryEmitter(_sreTypeSystem.CreateTypeBuilder(indexerClosureType))),
  165. _sreEmitMappings,
  166. _sreContextType) { EnableIlVerification = true };
  167. IXamlType overrideType = null;
  168. if (rootInstance != null)
  169. {
  170. overrideType = _sreTypeSystem.GetType(rootInstance.GetType());
  171. }
  172. compiler.IsDesignMode = isDesignMode;
  173. compiler.ParseAndCompile(xaml, uri?.ToString(), null, _sreTypeSystem.CreateTypeBuilder(tb), overrideType);
  174. var created = tb.CreateTypeInfo();
  175. clrPropertyBuilder.CreateTypeInfo();
  176. return LoadOrPopulate(created, rootInstance);
  177. }
  178. #endif
  179. static object LoadOrPopulate(Type created, object rootInstance)
  180. {
  181. var isp = Expression.Parameter(typeof(IServiceProvider));
  182. var epar = Expression.Parameter(typeof(object));
  183. var populate = created.GetMethod(AvaloniaXamlIlCompiler.PopulateName);
  184. isp = Expression.Parameter(typeof(IServiceProvider));
  185. var populateCb = Expression.Lambda<Action<IServiceProvider, object>>(
  186. Expression.Call(populate, isp, Expression.Convert(epar, populate.GetParameters()[1].ParameterType)),
  187. isp, epar).Compile();
  188. if (rootInstance == null)
  189. {
  190. var targetType = populate.GetParameters()[1].ParameterType;
  191. var overrideField = targetType.GetField("!XamlIlPopulateOverride",
  192. BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
  193. if (overrideField != null)
  194. {
  195. overrideField.SetValue(null,
  196. new Action<object>(
  197. target => { populateCb(XamlIlRuntimeHelpers.CreateRootServiceProviderV2(), target); }));
  198. try
  199. {
  200. return Activator.CreateInstance(targetType);
  201. }
  202. finally
  203. {
  204. overrideField.SetValue(null, null);
  205. }
  206. }
  207. var createCb = Expression.Lambda<Func<IServiceProvider, object>>(
  208. Expression.Convert(Expression.Call(
  209. created.GetMethod(AvaloniaXamlIlCompiler.BuildName), isp), typeof(object)), isp).Compile();
  210. return createCb(XamlIlRuntimeHelpers.CreateRootServiceProviderV2());
  211. }
  212. else
  213. {
  214. populateCb(XamlIlRuntimeHelpers.CreateRootServiceProviderV2(), rootInstance);
  215. return rootInstance;
  216. }
  217. }
  218. public static object Load(Stream stream, Assembly localAssembly, object rootInstance, Uri uri,
  219. bool isDesignMode)
  220. {
  221. string xaml;
  222. using (var sr = new StreamReader(stream))
  223. xaml = sr.ReadToEnd();
  224. #if RUNTIME_XAML_CECIL
  225. return LoadCecil(xaml, localAssembly, rootInstance, uri);
  226. #else
  227. return LoadSre(xaml, localAssembly, rootInstance, uri, isDesignMode);
  228. #endif
  229. }
  230. #if RUNTIME_XAML_CECIL
  231. private static Dictionary<string, (Action<IServiceProvider, object> populate, Func<IServiceProvider, object>
  232. build)>
  233. s_CecilCache =
  234. new Dictionary<string, (Action<IServiceProvider, object> populate, Func<IServiceProvider, object> build)
  235. >();
  236. private static string _cecilEmitDir;
  237. private static CecilTypeSystem _cecilTypeSystem;
  238. private static XamlIlLanguageTypeMappings _cecilMappings;
  239. private static XamlLanguageEmitMappings<IXamlILEmitter, XamlILNodeEmitResult> _cecilEmitMappings;
  240. private static XamlIlXmlnsMappings _cecilXmlns;
  241. private static bool _cecilInitialized;
  242. static void InitializeCecil()
  243. {
  244. if(_cecilInitialized)
  245. return;
  246. var path = Assembly.GetEntryAssembly().GetModules()[0].FullyQualifiedName;
  247. _cecilEmitDir = Path.Combine(Path.GetDirectoryName(path), "emit");
  248. Directory.CreateDirectory(_cecilEmitDir);
  249. var refs = new[] {path}.Concat(File.ReadAllLines(path + ".refs"));
  250. _cecilTypeSystem = new CecilTypeSystem(refs);
  251. (_cecilMappings, _cecilEmitMappings) = AvaloniaXamlIlLanguage.Configure(_cecilTypeSystem);
  252. _cecilXmlns = XamlIlXmlnsMappings.Resolve(_cecilTypeSystem, _cecilMappings);
  253. _cecilInitialized = true;
  254. }
  255. private static Dictionary<string, Type> _cecilGeneratedCache = new Dictionary<string, Type>();
  256. static object LoadCecil(string xaml, Assembly localAssembly, object rootInstance, Uri uri)
  257. {
  258. if (uri == null)
  259. throw new InvalidOperationException("Please, go away");
  260. InitializeCecil();
  261. IXamlType overrideType = null;
  262. if (rootInstance != null)
  263. {
  264. overrideType = _cecilTypeSystem.GetType(rootInstance.GetType().FullName);
  265. }
  266. var safeUri = uri.ToString()
  267. .Replace(":", "_")
  268. .Replace("/", "_")
  269. .Replace("?", "_")
  270. .Replace("=", "_")
  271. .Replace(".", "_");
  272. if (_cecilGeneratedCache.TryGetValue(safeUri, out var cached))
  273. return LoadOrPopulate(cached, rootInstance);
  274. var asm = _cecilTypeSystem.CreateAndRegisterAssembly(safeUri, new Version(1, 0),
  275. ModuleKind.Dll);
  276. var def = new TypeDefinition("XamlIlLoader", safeUri,
  277. TypeAttributes.Class | TypeAttributes.Public, asm.MainModule.TypeSystem.Object);
  278. var contextDef = new TypeDefinition("XamlIlLoader", safeUri + "_XamlIlContext",
  279. TypeAttributes.Class | TypeAttributes.Public, asm.MainModule.TypeSystem.Object);
  280. asm.MainModule.Types.Add(def);
  281. asm.MainModule.Types.Add(contextDef);
  282. var tb = _cecilTypeSystem.CreateTypeBuilder(def);
  283. var compiler = new AvaloniaXamlIlCompiler(new XamlIlTransformerConfiguration(_cecilTypeSystem,
  284. localAssembly == null ? null : _cecilTypeSystem.FindAssembly(localAssembly.GetName().Name),
  285. _cecilMappings, XamlIlXmlnsMappings.Resolve(_cecilTypeSystem, _cecilMappings),
  286. AvaloniaXamlIlLanguage.CustomValueConverter),
  287. _cecilEmitMappings,
  288. _cecilTypeSystem.CreateTypeBuilder(contextDef));
  289. compiler.ParseAndCompile(xaml, uri.ToString(), tb, overrideType);
  290. var asmPath = Path.Combine(_cecilEmitDir, safeUri + ".dll");
  291. using(var f = File.Create(asmPath))
  292. asm.Write(f);
  293. var loaded = Assembly.LoadFile(asmPath)
  294. .GetTypes().First(x => x.Name == safeUri);
  295. _cecilGeneratedCache[safeUri] = loaded;
  296. return LoadOrPopulate(loaded, rootInstance);
  297. }
  298. #endif
  299. }
  300. }