private static void ProcessAvaloniaResources(IAssemblyInformation asm, ITypeInformation[] asmTypes, List <AvaresInfo> avaresValues) { const string avaresToken = "Build:"; //or "Populate:" should work both ways void registeravares(string localUrl, string returnTypeFullName = "") { var globalUrl = $"avares://{asm.Name}{localUrl}"; if (!avaresValues.Any(v => v.GlobalUrl == globalUrl)) { var avres = new AvaresInfo { Assembly = asm, LocalUrl = localUrl, GlobalUrl = globalUrl, ReturnTypeFullName = returnTypeFullName }; avaresValues.Add(avres); } } var resType = asmTypes.FirstOrDefault(t => t.FullName == "CompiledAvaloniaXaml.!AvaloniaResources"); if (resType != null) { foreach (var res in resType.Methods.Where(m => m.Name.StartsWith(avaresToken))) { registeravares(res.Name.Replace(avaresToken, ""), res.ReturnTypeFullName ?? ""); } } //try add avares Embedded resources like image,stream and x:Class if (asm.ManifestResourceNames.Contains("!AvaloniaResources")) { try { using (var avaresStream = asm.GetManifestResourceStream("!AvaloniaResources")) using (var r = new BinaryReader(avaresStream)) { var ms = new MemoryStream(r.ReadBytes(r.ReadInt32())); var br = new BinaryReader(ms); int version = br.ReadInt32(); if (version == 1) { var assetDoc = XDocument.Load(ms); var ns = assetDoc.Root.GetDefaultNamespace(); var avaResEntries = assetDoc.Root.Element(ns.GetName("Entries")).Elements(ns.GetName("AvaloniaResourcesIndexEntry")) .Select(entry => new { Path = entry.Element(ns.GetName("Path")).Value, Offset = int.Parse(entry.Element(ns.GetName("Offset")).Value), Size = int.Parse(entry.Element(ns.GetName("Size")).Value) }).ToArray(); var xClassEntries = avaResEntries.FirstOrDefault(v => v.Path == "/!AvaloniaResourceXamlInfo"); //get information about x:Class resources if (xClassEntries != null && xClassEntries.Size > 0) { try { avaresStream.Seek(xClassEntries.Offset, SeekOrigin.Current); var xClassDoc = XDocument.Load(new MemoryStream(r.ReadBytes(xClassEntries.Size))); var xClassMappingNode = xClassDoc.Root.Element(xClassDoc.Root.GetDefaultNamespace().GetName("ClassToResourcePathIndex")); if (xClassMappingNode != null) { const string arraysNs = "http://schemas.microsoft.com/2003/10/Serialization/Arrays"; var keyvalueofss = XName.Get("KeyValueOfstringstring", arraysNs); var keyName = XName.Get("Key", arraysNs); var valueName = XName.Get("Value", arraysNs); var xClassMappings = xClassMappingNode.Elements(keyvalueofss) .Where(e => e.Elements(keyName).Any() && e.Elements(valueName).Any()) .Select(e => new { Type = e.Element(keyName).Value, Path = e.Element(valueName).Value, }).ToArray(); foreach (var xcm in xClassMappings) { var resultType = asmTypes.FirstOrDefault(t => t.FullName == xcm.Type); //if we need another check //if (resultType?.Methods?.Any(m => m.Name == "!XamlIlPopulate") ?? false) if (resultType != null) { //we set here base class like Style, Styles, UserControl so we can manage //resources in a common way later registeravares(xcm.Path, resultType.GetBaseType()?.FullName ?? ""); } } } } catch (Exception xClassEx) { Console.WriteLine($"Failed fetch avalonia x:class resources in {asm.Name}, {xClassEx.Message}"); } } //add other img/stream resources foreach (var entry in avaResEntries.Where(v => !v.Path.StartsWith("/!"))) { registeravares(entry.Path); } } } } catch (Exception ex) { Console.WriteLine($"Failed fetch avalonia resources in {asm.Name}, {ex.Message}"); } } }
public static Metadata ConvertMetadata(IMetadataReaderSession provider) { var types = new Dictionary <string, MetadataType>(); var typeDefs = new Dictionary <MetadataType, ITypeInformation>(); var metadata = new Metadata(); var resourceUrls = new List <string>(); var avaresValues = new List <AvaresInfo>(); var ignoredResExt = new[] { ".resources", ".rd.xml" }; bool skipRes(string res) => ignoredResExt.Any(r => res.EndsWith(r, StringComparison.OrdinalIgnoreCase)); PreProcessTypes(types, metadata); foreach (var asm in provider.Assemblies) { var aliases = new Dictionary <string, string[]>(); ProcessWellKnownAliases(asm, aliases); ProcessCustomAttributes(asm, aliases); foreach (var type in asm.Types.Where(x => !x.IsInterface && x.IsPublic)) { var mt = types[type.FullName] = ConvertTypeInfomation(type); typeDefs[mt] = type; metadata.AddType("clr-namespace:" + type.Namespace + ";assembly=" + asm.Name, mt); string[] nsAliases = null; if (aliases.TryGetValue(type.Namespace, out nsAliases)) { foreach (var alias in nsAliases) { metadata.AddType(alias, mt); } } } const string avaresToken = "Build:"; //or "Populate:" should work both ways foreach (var resType in asm.Types.Where(t => t.FullName == "CompiledAvaloniaXaml.!AvaloniaResources" || t.Name == "CompiledAvaloniaXaml.!AvaloniaResources")) { foreach (var res in resType.Methods.Where(m => m.Name.StartsWith(avaresToken))) { var localUrl = res.Name.Replace(avaresToken, ""); var avres = new AvaresInfo { Assembly = asm, LocalUrl = localUrl, GlobalUrl = $"avares://{asm.Name}{localUrl}", ReturnTypeFullName = res.ReturnTypeFullName ?? "" }; avaresValues.Add(avres); } } resourceUrls.AddRange(asm.ManifestResourceNames.Where(r => !skipRes(r)).Select(r => $"resm:{r}?assembly={asm.Name}")); } foreach (var type in types.Values) { ITypeInformation typeDef; typeDefs.TryGetValue(type, out typeDef); var ctors = typeDef?.Methods .Where(m => m.IsPublic && !m.IsStatic && m.Name == ".ctor" && m.Parameters.Count == 1); int level = 0; while (typeDef != null) { foreach (var prop in typeDef.Properties) { if (!prop.HasPublicGetter && !prop.HasPublicSetter) { continue; } var p = new MetadataProperty(prop.Name, types.GetValueOrDefault(prop.TypeFullName), types.GetValueOrDefault(typeDef.FullName), false, prop.IsStatic, prop.HasPublicGetter, prop.HasPublicSetter); type.Properties.Add(p); } //check for attached properties only on top level if (level == 0) { foreach (var methodDef in typeDef.Methods) { if (methodDef.Name.StartsWith("Set") && methodDef.IsStatic && methodDef.IsPublic && methodDef.Parameters.Count == 2) { var name = methodDef.Name.Substring(3); type.Properties.Add(new MetadataProperty(name, types.GetValueOrDefault(methodDef.Parameters[1].TypeFullName), types.GetValueOrDefault(typeDef.FullName), true, false, true, true)); } } } if (typeDef.FullName == "Avalonia.AvaloniaObject") { type.IsAvaloniaObjectType = true; } typeDef = typeDef.GetBaseType(); level++; } type.HasAttachedProperties = type.Properties.Any(p => p.IsAttached); type.HasStaticGetProperties = type.Properties.Any(p => p.IsStatic && p.HasGetter); type.HasSetProperties = type.Properties.Any(p => !p.IsStatic && p.HasSetter); if (ctors?.Any() == true) { bool supportType = ctors.Any(m => m.Parameters[0].TypeFullName == "System.Type"); bool supportObject = ctors.Any(m => m.Parameters[0].TypeFullName == "System.Object" || m.Parameters[0].TypeFullName == "System.String"); if (types.TryGetValue(ctors.First().Parameters[0].TypeFullName, out MetadataType parType) && parType.HasHintValues) { type.SupportCtorArgument = MetadataTypeCtorArgument.HintValues; type.HasHintValues = true; type.HintValues = parType.HintValues; } else if (supportType && supportObject) { type.SupportCtorArgument = MetadataTypeCtorArgument.TypeAndObject; } else if (supportType) { type.SupportCtorArgument = MetadataTypeCtorArgument.Type; } else if (supportObject) { type.SupportCtorArgument = MetadataTypeCtorArgument.Object; } } } PostProcessTypes(types, metadata, resourceUrls, avaresValues); return(metadata); }