Exemple #1
0
        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}");
                }
            }
        }
Exemple #2
0
        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);
        }