using System; using System.Collections; using System.Collections.Generic; using System.IO; using Mono.Cecil; namespace Xamarin.Bundler { public enum SymbolType { Function, ObjectiveCClass, Field, } public enum SymbolMode { Default, Linker, // pass "-u symbol" to the native linker Code, // generate code Ignore, // do nothing and hope for the best } public class Symbol { public SymbolType Type; public bool Ignore; static string ObjectiveCPrefix { get { return "OBJC_CLASS_$_"; } } string name; public string Name { get { if (name != null) return name; if (ObjectiveCName != null) return ObjectiveCPrefix + ObjectiveCName; throw ErrorHelper.CreateError (99, Errors.MX0099, $"symbol without a name (type: {Type})"); } set { name = value; if (name.StartsWith (ObjectiveCPrefix, StringComparison.Ordinal)) { ObjectiveCName = name.Substring (ObjectiveCPrefix.Length); name = null; } } } public string ObjectiveCName; public string Prefix { get { return "_"; } } List members = new List (); public IEnumerable Members { get { return members; } } public HashSet Assemblies { get; private set; } = new HashSet (); public void AddMember (MemberReference member) { members.Add (member); Assemblies.Add (member.Module.Assembly); } public void AddAssembly (AssemblyDefinition assembly) { Assemblies.Add (assembly); } } public class Symbols : IEnumerable { Dictionary store = new Dictionary (StringComparer.Ordinal); public int Count { get { return store.Count; } } public void Add (Symbol symbol) { store.Add (symbol.Name, symbol); } public Symbol AddObjectiveCClass (string class_name) { var symbol = new Symbol { Type = SymbolType.ObjectiveCClass, ObjectiveCName = class_name, }; var existing = Find (symbol.Name); if (existing != null) return existing; Add (symbol); return symbol; } public Symbol AddField (string name) { Symbol rv = Find (name); if (rv == null) { rv = new Symbol { Name = name, Type = SymbolType.Field }; Add (rv); } return rv; } public Symbol AddFunction (string name) { Symbol rv = Find (name); if (rv == null) { rv = new Symbol { Name = name, Type = SymbolType.Function }; Add (rv); } return rv; } public void Remove (Func condition) { foreach (var symbol in this) { if (condition (symbol)) store.Remove (symbol.Name); } } public IEnumerator GetEnumerator () { return store.Values.GetEnumerator (); } IEnumerator IEnumerable.GetEnumerator () { return store.Values.GetEnumerator (); } public Symbol Find (string name) { Symbol rv; store.TryGetValue (name, out rv); return rv; } public bool Contains (string name) { return store.ContainsKey (name); } public Symbol this [string name] { get { return store [name]; } } public void Load (string filename, Target target) { using (var reader = new StreamReader (filename)) { string line; Symbol current = null; while ((line = reader.ReadLine ()) != null) { if (line.Length == 0) continue; if (line [0] == '\t') { var asm = line.Substring (1); Assembly assembly; if (!target.Assemblies.TryGetValue (Assembly.GetIdentity (asm), out assembly)) throw ErrorHelper.CreateError (99, Errors.MX0099, $"serialized assembly {asm} for symbol {current.Name}, but no such assembly loaded"); current.AddAssembly (assembly.AssemblyDefinition); } else { var eq = line.IndexOf ('='); var typestr = line.Substring (0, eq); var name = line.Substring (eq + 1); current = new Symbol { Name = name, Type = (SymbolType) Enum.Parse (typeof (SymbolType), typestr) }; Add (current); } } } } public void Save (string filename) { using (var writer = new StreamWriter (filename)) { foreach (var symbol in store.Values) { writer.WriteLine ("{0}={1}", symbol.Type, symbol.Name); foreach (var asm in symbol.Assemblies) writer.WriteLine ($"\t{asm.MainModule.FileName}"); } } } } }