From 56a1e5bdd8622c20ff46ed359be588d7c763cde3 Mon Sep 17 00:00:00 2001 From: Alex Soto Date: Thu, 5 May 2016 00:20:49 -0500 Subject: [PATCH] [generator] Fix bug 17232 - Invalid class name generated in the Libraries.g.cs file https://bugzilla.xamarin.com/show_bug.cgi?id=17232 * Added support for specifying library path in FieldAttribute * Fixed generator error when Namespaces contains dots `.` * Added error BI1042 Missing '[Field (LibraryName=value)]' for {field_pi.Name} (e.g."__Internal") instead of generating invalid c# code when no LibraryName is provided in 3rd party bindings * Kept support for just using the system library name in FieldAttribute (i.e. [Field ("UnboundFooSymbol", "UIKit")] This does not change our current generated code at all: https://gist.github.com/dalexsoto/338464a260bc6971e7b665ca9463e8b9 --- src/error.cs | 1 + src/generator-enums.cs | 5 ++-- src/generator.cs | 62 ++++++++++++++++++++++++++++++++++-------- 3 files changed, 55 insertions(+), 13 deletions(-) diff --git a/src/error.cs b/src/error.cs index c103f129bacf..c6bd9cfe404b 100644 --- a/src/error.cs +++ b/src/error.cs @@ -56,6 +56,7 @@ // BI1039 The selector {0} on type {1} is found multiple times with different argument length {2} : {3}. // BI1040 The selector {0} on type {1} is found multiple times with different argument out states on argument {2}. // BI1041 The selector {0} on type {1} is found multiple times with different argument types on argument {2} - {3} : {4}. +// BI1042 Missing '[Field (LibraryName=value)]' for {field_pi.Name} (e.g."__Internal") // BI11xx warnings // BI1101 Trying to use a string as a [Target] // BI1102 Using the deprecated EventArgs for a delegate signature in {0}.{1}, please use DelegateName instead diff --git a/src/generator-enums.cs b/src/generator-enums.cs index 904e5552a998..47439d7061c3 100644 --- a/src/generator-enums.cs +++ b/src/generator-enums.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Reflection; using XamCore.Foundation; using XamCore.ObjCRuntime; @@ -96,8 +97,8 @@ void GenerateEnum (Type type) library_name = library_name.Substring (ns.Prefix.Length + 1); // there might not be any other fields in the framework - if (!libraries.Contains (library_name)) - libraries.Add (library_name); + if (!libraries.ContainsKey (library_name)) + libraries.Add (library_name, null); } if (error != null) { diff --git a/src/generator.cs b/src/generator.cs index 575f8ce1be21..b135c9a061c6 100644 --- a/src/generator.cs +++ b/src/generator.cs @@ -1485,7 +1485,7 @@ public partial class Generator : IMemberGatherer { Dictionary trampolines = new Dictionary (); Dictionary trampolines_generic_versions = new Dictionary (); Dictionary notification_event_arg_types = new Dictionary (); - List libraries = new List (); + Dictionary libraries = new Dictionary (); // List> async_result_types = new List> (); HashSet async_result_types_emitted = new HashSet (); @@ -3004,7 +3004,30 @@ void GenerateTrampolinesForQueue (TrampolineInfo [] queue) print ("}} /* class {0} */", ti.NativeInvokerName); } } - + + // We need to check against the user using just UIKit (and friends) in the FieldAttribute + // so we need to reflect the libraries contained in our Constants class and do the mapping + // we will return the system library path if found + bool IsNotSystemLibrary (string library_name) + { + string library_path = null; + return TryGetLibraryPath (library_name, ref library_path); + } + + bool TryGetLibraryPath (string library_name, ref string library_path) + { + var libSuffixedName = $"{library_name}Library"; + var constType = typeof ( +#if XAMCORE_2_0 + XamCore.ObjCRuntime.Constants); +#else + XamCore.Constants); +#endif + var field = constType.GetFields (BindingFlags.Public | BindingFlags.Static).FirstOrDefault (f => f.Name == libSuffixedName); + library_path = (string) field?.GetValue (null); + return library_path == null; + } + void GenerateLibraryHandles () { sw = GetOutputStream ("ObjCRuntime", "Libraries"); @@ -3013,10 +3036,14 @@ void GenerateLibraryHandles () print ("namespace {0} {{", ns.CoreObjCRuntime); indent++; print ("[CompilerGenerated]"); print ("static partial class Libraries {"); indent++; - foreach (string library_name in libraries.OrderBy (v => v)) { - print ("static public class {0} {{", library_name); indent++; + foreach (var library_info in libraries.OrderBy (v => v.Key)) { + var library_name = library_info.Key; + var library_path = library_info.Value; + print ("static public class {0} {{", library_name.Replace (".", string.Empty)); indent++; if (BindThirdPartyLibrary && library_name == "__Internal") { print ("static public readonly IntPtr Handle = Dlfcn.dlopen (null, 0);"); + } else if (BindThirdPartyLibrary && library_path != null && IsNotSystemLibrary (library_name)) { + print ($"static public readonly IntPtr Handle = Dlfcn.dlopen (\"{library_path}\", 0);"); } else { print ("static public readonly IntPtr Handle = Dlfcn.dlopen (Constants.{0}Library, 0);", library_name); } @@ -6228,6 +6255,7 @@ public void Generate (Type type) foreach (var field_pi in field_exports.OrderBy (f => f.Name)) { var fieldAttr = (FieldAttribute) field_pi.GetCustomAttributes (typeof (FieldAttribute), true) [0]; string library_name; + string library_path = null; if (fieldAttr.LibraryName != null){ // Remapped @@ -6241,19 +6269,31 @@ public void Generate (Type type) library_name = CoreServicesMap; break; } - } + } else { + // we get something in LibraryName from FieldAttribute so we asume + // it is a path to a library, so we save the path and change library name + // to a valid identifier if needed + library_path = library_name; + library_name = Path.GetFileName (library_name); + if (library_name.Contains (".")) + library_name = library_name.Replace (".", string.Empty); + } + } else if (BindThirdPartyLibrary) { + // User should provide a LibraryName + throw new BindingException (1042, true, $"Missing '[Field (LibraryName=value)]' for {field_pi.Name} (e.g.\"__Internal\")"); } else { library_name = type.Namespace; // note: not every binding namespace will start with ns.Prefix (e.g. MonoTouch.) - if (!String.IsNullOrEmpty (ns.Prefix) && library_name.StartsWith (ns.Prefix)) + if (!String.IsNullOrEmpty (ns.Prefix) && library_name.StartsWith (ns.Prefix)) { library_name = library_name.Substring (ns.Prefix.Length + 1); + library_name = library_name.Replace (".", string.Empty); // Remove dots from namespaces + } } - if (!libraries.Contains (library_name)) { - libraries.Add (library_name); - } + if (!libraries.ContainsKey (library_name)) + libraries.Add (library_name, library_path); + bool is_unified_internal = field_pi.IsUnifiedInternal (); - string fieldTypeName = FormatType (field_pi.DeclaringType, field_pi.PropertyType); // Value types we dont cache for now, to avoid Nullable if (!field_pi.PropertyType.IsValueType) { @@ -6263,7 +6303,7 @@ public void Generate (Type type) } PrintPreserveAttribute (field_pi); - print ("[Field (\"{0}\", \"{1}\")]", fieldAttr.SymbolName, library_name); + print ("[Field (\"{0}\", \"{1}\")]", fieldAttr.SymbolName, library_path ?? library_name); PrintPlatformAttributes (field_pi); if (Generator.HasAttribute (field_pi, typeof (AdvancedAttribute))){ print ("[EditorBrowsable (EditorBrowsableState.Advanced)]");