Merge pull request #3462 from rolfbjarne/optimized-protocol-support

Add a 'register-protocols' optimization that:

Improves static registrar to:

* Generate a new table of protocol -> managed wrapper type. This is required
  to find the wrapper type without having the `[Protocol]` attribute around.

* Make the generated code implement protocols from [Adopts] attributes. This
  makes it possible to link away the `[Protocol]` attribute, because the
  native implementation of `conformsToProtocol:` does the right thing (we
  might even be able to link away our complete `ConformsToProtocol` logic when
  we remove the dynamic registrar).

Improves linker to:

* Not mark protocol interfaces by the mere virtue of having a type that
  implements them. This is implemented by not marking protocol interfaces when
  they're implementing a class, but instead when a method implementation is
  found to implement a method from a protocol interface.

* Mark the wrapper type for protocols (this allows us to remove the Protocol
  attribute, since that's the link between the protocol and its wrapper type).

* Remove the [Protocol], [ProtocolMember] and [Adopts] attributes (but only if
  optimizing protocols).

The static registrar still needs some of the information linked away, so a few
changes are required to make it available post linker.

Benchmark
---------

I've compared the size of entire apps built for device:

|test                          | Before |  After |   Diff |     % |
|:-----------------------------|-------:|-------:|-------:|------:|
|[monotouch-test/Debug][1]     |  101mb |  100mb | -888kb | -0.9% |
|[monotouch-test/Release][2]   | 99.2mb | 95.4mb | -830kb | -0.9% |
|[minimalistic app/Debug][3]   | 10.8mb | 10.4mb | -443kb | -4.1% |
|[minimalistic app/Release][4] |  4.7mb | 4.55mb | -157kb | -3.3% |

[1]: https://gist.github.com/rolfbjarne/0181ab8abe436c34cf4ee68ecfb8cd18#monotouch-test-debug
[2]: https://gist.github.com/rolfbjarne/0181ab8abe436c34cf4ee68ecfb8cd18#monotouch-test-release
[3]: https://gist.github.com/rolfbjarne/0181ab8abe436c34cf4ee68ecfb8cd18#minimal-xi-app-debug
[4]: https://gist.github.com/rolfbjarne/0181ab8abe436c34cf4ee68ecfb8cd18#minimal-xi-app-release
This commit is contained in:
Rolf Bjarne Kvinge 2018-02-14 17:47:31 +01:00 коммит произвёл GitHub
Родитель 0d9b598cc2 a6106fb3a6
Коммит 60a8731b35
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
36 изменённых файлов: 1077 добавлений и 144 удалений

Просмотреть файл

@ -367,3 +367,11 @@ See the [equivalent mtouch warning](mtouch-errors.md#MT5218).
<!-- 8016 used by mtouch --> <!-- 8016 used by mtouch -->
### <a name="MM8017">MM8017: The Boehm garbage collector is not supported. Please use SGen instead. ### <a name="MM8017">MM8017: The Boehm garbage collector is not supported. Please use SGen instead.
### <a name="MM8025"/>MM8025: Failed to compute the token reference for the type '{type.AssemblyQualifiedName}' because {reasons}
This indicates a bug in Xamarin.Mac. Please file a bug at [https://bugzilla.xamarin.com](https://bugzilla.xamarin.com/enter_bug.cgi?product=Xamarin.Mac).
A potential workaround would be to disable the `register-protocols`
optimization, by passing `--optimize:-register-protocols` as an additional mmp
argument in the project's Mac Build options.

Просмотреть файл

@ -2377,3 +2377,10 @@ This indicates a bug in Xamarin.iOS. Please file a bug at [http://bugzilla.xamar
This indicates a bug in Xamarin.iOS. Please file a bug at [http://bugzilla.xamarin.com](https://bugzilla.xamarin.com/enter_bug.cgi?product=iOS). This indicates a bug in Xamarin.iOS. Please file a bug at [http://bugzilla.xamarin.com](https://bugzilla.xamarin.com/enter_bug.cgi?product=iOS).
### <a name="MT8025"/>MT8025: Failed to compute the token reference for the type '{type.AssemblyQualifiedName}' because {reasons}
This indicates a bug in Xamarin.iOS. Please file a bug at [https://bugzilla.xamarin.com](https://bugzilla.xamarin.com/enter_bug.cgi?product=iOS).
A potential workaround would be to disable the `register-protocols`
optimization, by passing `--optimize:-register-protocols` as an additional
mtouch argument in the project's iOS Build options.

Просмотреть файл

@ -348,3 +348,35 @@ the static registrar is enabled by default for release builds).
The default behavior can be overridden by passing `--optimize=[+|-]blockliteral-setupblock` to mtouch/mmp. The default behavior can be overridden by passing `--optimize=[+|-]blockliteral-setupblock` to mtouch/mmp.
Optimize support for protocols
------------------------------
The Xamarin.iOS/Mac runtime needs information about how managed types
implements Objective-C protocols. This information is stored in interfaces
(and attributes on these interfaces), which is not a very efficient format,
nor is it linker-friendly.
One example is that these interfaces store information about all protocol
members in a `[ProtocolMember]` attribute, which among other things contain
references to the parameter types of those members. This means that simply
implementing such an interface will make the linker preserve all types used in
that interface, even for optional members the app never calls or implements.
This optimization will make the static registrar store any required
information in an efficient format that uses little memory that's easy and
quick to find at runtime.
It will also teach the linker that it does not necessarily need to preserve
these interfaces, nor any of the related attributes.
This optimization requires both the linker and the static registrar to be
enabled.
On Xamarin.iOS this optimization is enabled by default when both the linker
and the static registrar are enabled.
On Xamarin.Mac this optimization is never enabled by default, because
Xamarin.Mac supports loading assemblies dynamically, and those assemblies
might not have been known at build time (and thus not optimized).
The default behavior can be overridden by passing `--optimize=-register-protocols` to mtouch/mmp.

Просмотреть файл

@ -1213,6 +1213,28 @@ print_callback (const char *string, mono_bool is_stdout)
PRINT ("%s", string); PRINT ("%s", string);
} }
static int
xamarin_compare_ints (const void *a, const void *b)
{
uint32_t x = *(uint32_t *) a;
uint32_t y = *(uint32_t *) b;
return x < y ? -1 : (x == y ? 0 : 1);
}
uint32_t
xamarin_find_protocol_wrapper_type (uint32_t token_ref)
{
if (options.RegistrationData == NULL || options.RegistrationData->protocol_wrappers == NULL)
return INVALID_TOKEN_REF;
void* ptr = bsearch (&token_ref, options.RegistrationData->protocol_wrappers, options.RegistrationData->protocol_wrapper_count, sizeof (MTProtocolWrapperMap), xamarin_compare_ints);
if (ptr == NULL)
return INVALID_TOKEN_REF;
MTProtocolWrapperMap *entry = (MTProtocolWrapperMap *) ptr;
return entry->wrapper_token;
}
void void
xamarin_initialize_embedded () xamarin_initialize_embedded ()
{ {

Просмотреть файл

@ -77,6 +77,16 @@ typedef struct __attribute__((packed)) {
uint32_t /* index into MTRegistrationMap->map */ index; uint32_t /* index into MTRegistrationMap->map */ index;
} MTManagedClassMap; } MTManagedClassMap;
typedef struct __attribute__((packed)) {
uint32_t protocol_token;
uint32_t wrapper_token;
} MTProtocolWrapperMap;
typedef struct __attribute__((packed)) {
const uint32_t *protocol_tokens; // an array of token references to managed interfaces that represent protocols
const Protocol **protocols; // the corresponding native protocols
} MTProtocolMap;
struct MTRegistrationMap; struct MTRegistrationMap;
struct MTRegistrationMap { struct MTRegistrationMap {
@ -93,11 +103,15 @@ struct MTRegistrationMap {
// ObjC class, but this is not a constant known at compile time, which // ObjC class, but this is not a constant known at compile time, which
// means it can't be stored in read-only memory). // means it can't be stored in read-only memory).
const MTManagedClassMap *skipped_map; const MTManagedClassMap *skipped_map;
const MTProtocolWrapperMap *protocol_wrappers; // array of MTProtocolWrapperMap, sorted ascending by protocol_token
const MTProtocolMap protocols;
int assembly_count; int assembly_count;
int map_count; int map_count;
int custom_type_count; int custom_type_count;
int full_token_reference_count; int full_token_reference_count;
int skipped_map_count; int skipped_map_count;
int protocol_wrapper_count;
int protocol_count;
}; };
typedef struct { typedef struct {
@ -181,6 +195,7 @@ void xamarin_ftnptr_exception_handler (guint32 gchandle);
void xamarin_create_classes (); void xamarin_create_classes ();
const char * xamarin_skip_encoding_flags (const char *encoding); const char * xamarin_skip_encoding_flags (const char *encoding);
void xamarin_add_registration_map (struct MTRegistrationMap *map); void xamarin_add_registration_map (struct MTRegistrationMap *map);
uint32_t xamarin_find_protocol_wrapper_type (uint32_t token_ref);
bool xamarin_has_managed_ref (id self); bool xamarin_has_managed_ref (id self);
bool xamarin_has_managed_ref_safe (id self); bool xamarin_has_managed_ref_safe (id self);

Просмотреть файл

@ -376,6 +376,66 @@ namespace ObjCRuntime {
throw ErrorHelper.CreateError (8019, $"Could not find the assembly {assembly_name} in the loaded assemblies."); throw ErrorHelper.CreateError (8019, $"Could not find the assembly {assembly_name} in the loaded assemblies.");
} }
internal unsafe static uint GetTokenReference (Type type)
{
if (type.IsGenericType)
type = type.GetGenericTypeDefinition ();
var asm_name = type.Module.Assembly.GetName ().Name;
// First check if there's a full token reference to this type
var token = GetFullTokenReference (asm_name, type.Module.MetadataToken, type.MetadataToken);
if (token != uint.MaxValue)
return token;
// If type.Module.MetadataToken != 1, then the token must be a full token, which is not the case because we've already checked, so throw an exception.
if (type.Module.MetadataToken != 1)
throw ErrorHelper.CreateError (8025, $"Failed to compute the token reference for the type '{type.AssemblyQualifiedName}' because its module's metadata token is {type.Module.MetadataToken} when expected 1.");
var map = Runtime.options->RegistrationMap;
// Find the assembly index in our list of registered assemblies.
int assembly_index = -1;
for (int i = 0; i < map->assembly_count; i++) {
var name_ptr = Marshal.ReadIntPtr (map->assembly, (int) i * IntPtr.Size);
if (Runtime.StringEquals (name_ptr, asm_name)) {
assembly_index = i;
break;
}
}
// If the assembly isn't registered, then the token must be a full token (which it isn't, because we've already checked).
if (assembly_index == -1)
throw ErrorHelper.CreateError (8025, $"Failed to compute the token reference for the type '{type.AssemblyQualifiedName}' because the assembly couldn't be found in the list of registered assemblies.");
if (assembly_index > 127)
throw ErrorHelper.CreateError (8025, $"Failed to compute the token reference for the type '{type.AssemblyQualifiedName}' because the assembly index {assembly_index} is not valid (must be <= 127).");
return (uint) ((type.MetadataToken << 8) + (assembly_index << 1));
}
// Look for the specified metadata token in the table of full token references.
static unsafe uint GetFullTokenReference (string assembly_name, int module_token, int metadata_token)
{
var map = Runtime.options->RegistrationMap;
for (int i = 0; i < map->full_token_reference_count; i++) {
var ptr = map->full_token_references + (i * IntPtr.Size + 8);
var asm_ptr = Marshal.ReadIntPtr (ptr);
var token = Marshal.ReadInt32 (ptr + IntPtr.Size + 4);
if (token != metadata_token)
continue;
var mod_token = Marshal.ReadInt32 (ptr + IntPtr.Size);
if (mod_token != module_token)
continue;
if (!Runtime.StringEquals (asm_ptr, assembly_name))
continue;
return (uint) i;
}
return uint.MaxValue;
}
/* /*
Type must have been previously registered. Type must have been previously registered.
*/ */

Просмотреть файл

@ -311,7 +311,7 @@ namespace Registrar {
return SharedDynamic.GetOneAttribute<RegisterAttribute> (type); return SharedDynamic.GetOneAttribute<RegisterAttribute> (type);
} }
protected override ProtocolAttribute GetProtocolAttribute (Type type) public override ProtocolAttribute GetProtocolAttribute (Type type)
{ {
return SharedDynamic.GetOneAttribute<ProtocolAttribute> (type); return SharedDynamic.GetOneAttribute<ProtocolAttribute> (type);
} }
@ -378,6 +378,11 @@ namespace Registrar {
return attr == null ? null : attr.WrapperType; return attr == null ? null : attr.WrapperType;
} }
protected override IList<AdoptsAttribute> GetAdoptsAttributes (Type type)
{
return (AdoptsAttribute[]) type.GetCustomAttributes (typeof (AdoptsAttribute), false);
}
protected override string GetAssemblyName (Assembly assembly) protected override string GetAssemblyName (Assembly assembly)
{ {
return assembly.GetName ().Name; return assembly.GetName ().Name;

Просмотреть файл

@ -26,15 +26,7 @@ namespace ObjCRuntime {
public Protocol (Type type) public Protocol (Type type)
{ {
if (type.IsInterface) { this.handle = Runtime.GetProtocolForType (type);
foreach (var pa in type.GetCustomAttributes<ProtocolAttribute> (false)) {
handle = objc_getProtocol (pa.Name);
if (handle != IntPtr.Zero)
return;
}
}
if (handle == IntPtr.Zero)
throw new ArgumentException (string.Format ("'{0}' is an unknown protocol", type.FullName));
} }
public Protocol (IntPtr handle) public Protocol (IntPtr handle)

Просмотреть файл

@ -135,6 +135,7 @@ namespace Registrar {
public TType Type; public TType Type;
public ObjCType BaseType; public ObjCType BaseType;
public ObjCType [] Protocols; public ObjCType [] Protocols;
public string [] AdoptedProtocols;
public bool IsModel; public bool IsModel;
// if this type represents an ObjC protocol (!= has the protocol attribute, since that can be applied to all kinds of things). // if this type represents an ObjC protocol (!= has the protocol attribute, since that can be applied to all kinds of things).
public bool IsProtocol; public bool IsProtocol;
@ -143,6 +144,8 @@ namespace Registrar {
public bool IsGeneric; public bool IsGeneric;
#if !MTOUCH && !MMP #if !MTOUCH && !MMP
public IntPtr Handle; public IntPtr Handle;
#else
public TType ProtocolWrapperType;
#endif #endif
public Dictionary<string, ObjCField> Fields; public Dictionary<string, ObjCField> Fields;
@ -998,6 +1001,7 @@ namespace Registrar {
protected abstract bool ContainsPlatformReference (TAssembly assembly); // returns true if the assembly is monotouch.dll too. protected abstract bool ContainsPlatformReference (TAssembly assembly); // returns true if the assembly is monotouch.dll too.
protected abstract TType GetBaseType (TType type); // for generic parameters it returns the first specific class constraint. protected abstract TType GetBaseType (TType type); // for generic parameters it returns the first specific class constraint.
protected abstract TType[] GetInterfaces (TType type); // may return interfaces from base classes as well. May return null if no interfaces found. protected abstract TType[] GetInterfaces (TType type); // may return interfaces from base classes as well. May return null if no interfaces found.
protected virtual TType [] GetLinkedAwayInterfaces (TType type) { return null; } // may NOT return interfaces from base classes as well. May return null if no interfaces found.
protected abstract TMethod GetBaseMethod (TMethod method); protected abstract TMethod GetBaseMethod (TMethod method);
protected abstract TType[] GetParameters (TMethod method); protected abstract TType[] GetParameters (TMethod method);
protected abstract string GetParameterName (TMethod method, int parameter_index); protected abstract string GetParameterName (TMethod method, int parameter_index);
@ -1022,13 +1026,14 @@ namespace Registrar {
public abstract RegisterAttribute GetRegisterAttribute (TType type); // Return null if no attribute is found. Do not consider base types. public abstract RegisterAttribute GetRegisterAttribute (TType type); // Return null if no attribute is found. Do not consider base types.
protected abstract CategoryAttribute GetCategoryAttribute (TType type); // Return null if no attribute is found. Do not consider base types. protected abstract CategoryAttribute GetCategoryAttribute (TType type); // Return null if no attribute is found. Do not consider base types.
protected abstract ConnectAttribute GetConnectAttribute (TProperty property); // Return null if no attribute is found. Do not consider inherited properties. protected abstract ConnectAttribute GetConnectAttribute (TProperty property); // Return null if no attribute is found. Do not consider inherited properties.
protected abstract ProtocolAttribute GetProtocolAttribute (TType type); // Return null if no attribute is found. Do not consider base types. public abstract ProtocolAttribute GetProtocolAttribute (TType type); // Return null if no attribute is found. Do not consider base types.
protected abstract IEnumerable<ProtocolMemberAttribute> GetProtocolMemberAttributes (TType type); // Return null if no attributes found. Do not consider base types. protected abstract IEnumerable<ProtocolMemberAttribute> GetProtocolMemberAttributes (TType type); // Return null if no attributes found. Do not consider base types.
protected abstract List<AvailabilityBaseAttribute> GetAvailabilityAttributes (TType obj); // must only return attributes for the current platform. protected abstract List<AvailabilityBaseAttribute> GetAvailabilityAttributes (TType obj); // must only return attributes for the current platform.
protected abstract Version GetSDKVersion (); protected abstract Version GetSDKVersion ();
protected abstract TType GetProtocolAttributeWrapperType (TType type); // Return null if no attribute is found. Do not consider base types. protected abstract TType GetProtocolAttributeWrapperType (TType type); // Return null if no attribute is found. Do not consider base types.
protected abstract BindAsAttribute GetBindAsAttribute (TMethod method, int parameter_index); // If parameter_index = -1 then get the attribute for the return type. Return null if no attribute is found. Must consider base method. protected abstract BindAsAttribute GetBindAsAttribute (TMethod method, int parameter_index); // If parameter_index = -1 then get the attribute for the return type. Return null if no attribute is found. Must consider base method.
protected abstract BindAsAttribute GetBindAsAttribute (TProperty property); protected abstract BindAsAttribute GetBindAsAttribute (TProperty property);
protected abstract IList<AdoptsAttribute> GetAdoptsAttributes (TType type);
public abstract TType GetNullableType (TType type); // For T? returns T. For T returns null. public abstract TType GetNullableType (TType type); // For T? returns T. For T returns null.
protected abstract bool HasReleaseAttribute (TMethod method); // Returns true of the method's return type/value has a [Release] attribute. protected abstract bool HasReleaseAttribute (TMethod method); // Returns true of the method's return type/value has a [Release] attribute.
protected abstract bool IsINativeObject (TType type); protected abstract bool IsINativeObject (TType type);
@ -1594,6 +1599,8 @@ namespace Registrar {
// return interfaces from all base classes as well. // return interfaces from all base classes as well.
// We only want the interfaces declared on this type. // We only want the interfaces declared on this type.
// Additionally it may return interface implementations that were linked away.
// This function will return arrays with null entries. // This function will return arrays with null entries.
var type = objcType.Type; var type = objcType.Type;
@ -1625,30 +1632,87 @@ namespace Registrar {
ObjCType [] GetProtocols (ObjCType type, ref List<Exception> exceptions) ObjCType [] GetProtocols (ObjCType type, ref List<Exception> exceptions)
{ {
var interfaces = GetInterfacesImpl (type); var interfaces = GetInterfacesImpl (type);
if (interfaces == null || interfaces.Length == 0) List<ObjCType> protocolList = null;
return null; if (interfaces?.Length > 0) {
var ifaceList = new List<TType> (interfaces);
var ifaceList = new List<TType> (interfaces); protocolList = new List<ObjCType> (interfaces.Length);
var protocolList = new List<ObjCType> (interfaces.Length); for (int i = 0; i < ifaceList.Count; i++) {
for (int i = 0; i < ifaceList.Count; i++) { if (ifaceList [i] == null)
if (ifaceList [i] == null) continue;
continue; var baseP = RegisterTypeUnsafe (ifaceList [i], ref exceptions);
var baseP = RegisterTypeUnsafe (ifaceList [i], ref exceptions); if (baseP != null) {
if (baseP != null) { if (baseP.IsInformalProtocol) {
if (baseP.IsInformalProtocol) { var ifaces = GetInterfacesImpl (baseP);
var ifaces = GetInterfacesImpl (baseP); if (ifaces != null)
if (ifaces != null) ifaceList.AddRange (ifaces);
ifaceList.AddRange (ifaces); } else {
} else { protocolList.Add (baseP);
protocolList.Add (baseP); }
} }
} }
} }
if (protocolList.Count == 0)
// The interface might have been linked away.
var linkedAwayInterfaces = new List<TType> ();
var laifaces = GetLinkedAwayInterfaces (type.Type);
var baseType = type.BaseType;
if (laifaces != null)
linkedAwayInterfaces.AddRange (laifaces);
while (baseType?.IsModel == true) {
laifaces = GetLinkedAwayInterfaces (baseType.Type);
if (laifaces != null)
linkedAwayInterfaces.AddRange (laifaces);
baseType = baseType.BaseType;
}
if (linkedAwayInterfaces.Count > 0) {
if (protocolList == null)
protocolList = new List<ObjCType> ();
for (int i = 0; i < linkedAwayInterfaces.Count; i++) {
var iface = linkedAwayInterfaces [i];
var proto = GetProtocolAttribute (iface);
if (proto == null)
continue;
if (proto.IsInformal) {
laifaces = GetLinkedAwayInterfaces (iface);
if (laifaces != null)
linkedAwayInterfaces.AddRange (laifaces);
continue;
}
// We can't register this interface (because it's been linked away),
// but we still need to return information about it.
// So create an ObjCType with just the required information,
// and add that to what we return.
var objcType = new ObjCType () {
Registrar = this,
Type = iface,
IsProtocol = true,
};
#if MMP || MTOUCH
objcType.ProtocolWrapperType = GetProtocolAttributeWrapperType (objcType.Type);
objcType.IsWrapper = objcType.ProtocolWrapperType != null;
#endif
protocolList.Add (objcType);
}
}
if (protocolList == null || protocolList.Count == 0)
return null; return null;
return protocolList.ToArray (); return protocolList.ToArray ();
} }
string [] GetAdoptedProtocols (ObjCType type)
{
var attribs = GetAdoptsAttributes (type.Type);
if (attribs == null || attribs.Count == 0)
return null;
var rv = new string [attribs.Count];
for (var i = 0; i < attribs.Count; i++)
rv [i] = attribs [i].ProtocolType;
return rv;
}
ObjCType RegisterCategory (TType type, CategoryAttribute attrib, ref List<Exception> exceptions) ObjCType RegisterCategory (TType type, CategoryAttribute attrib, ref List<Exception> exceptions)
{ {
if (IsINativeObject (type)) { if (IsINativeObject (type)) {
@ -1832,8 +1896,12 @@ namespace Registrar {
IsGeneric = isGenericType, IsGeneric = isGenericType,
}; };
objcType.VerifyRegisterAttribute (ref exceptions); objcType.VerifyRegisterAttribute (ref exceptions);
objcType.Protocols = GetProtocols (objcType, ref exceptions); objcType.AdoptedProtocols = GetAdoptedProtocols (objcType);
objcType.BaseType = isProtocol ? null : (baseObjCType ?? objcType); objcType.BaseType = isProtocol ? null : (baseObjCType ?? objcType);
objcType.Protocols = GetProtocols (objcType, ref exceptions);
#if MMP || MTOUCH
objcType.ProtocolWrapperType = (isProtocol && !isInformalProtocol) ? GetProtocolAttributeWrapperType (objcType.Type) : null;
#endif
objcType.IsWrapper = (isProtocol && !isInformalProtocol) ? (GetProtocolAttributeWrapperType (objcType.Type) != null) : (objcType.RegisterAttribute != null && objcType.RegisterAttribute.IsWrapper); objcType.IsWrapper = (isProtocol && !isInformalProtocol) ? (GetProtocolAttributeWrapperType (objcType.Type) != null) : (objcType.RegisterAttribute != null && objcType.RegisterAttribute.IsWrapper);
if (!objcType.IsWrapper && objcType.BaseType != null) if (!objcType.IsWrapper && objcType.BaseType != null)

Просмотреть файл

@ -12,6 +12,7 @@ using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Foundation; using Foundation;
@ -51,16 +52,22 @@ namespace ObjCRuntime {
internal static DynamicRegistrar Registrar; internal static DynamicRegistrar Registrar;
const uint INVALID_TOKEN_REF = 0xFFFFFFFF;
internal unsafe struct MTRegistrationMap { internal unsafe struct MTRegistrationMap {
public IntPtr assembly; public IntPtr assembly;
public MTClassMap *map; public MTClassMap *map;
public IntPtr full_token_references; /* array of MTFullTokenReference */ public IntPtr full_token_references; /* array of MTFullTokenReference */
public MTManagedClassMap* skipped_map; public MTManagedClassMap* skipped_map;
public MTProtocolWrapperMap* protocol_wrapper_map;
public MTProtocolMap protocol_map;
public int assembly_count; public int assembly_count;
public int map_count; public int map_count;
public int custom_type_count; public int custom_type_count;
public int full_token_reference_count; public int full_token_reference_count;
public int skipped_map_count; public int skipped_map_count;
public int protocol_wrapper_count;
public int protocol_count;
} }
[StructLayout (LayoutKind.Sequential, Pack = 1)] [StructLayout (LayoutKind.Sequential, Pack = 1)]
@ -76,6 +83,18 @@ namespace ObjCRuntime {
public uint index; // index into MTRegistrationMap.map public uint index; // index into MTRegistrationMap.map
} }
[StructLayout (LayoutKind.Sequential, Pack = 1)]
internal struct MTProtocolWrapperMap {
public uint protocol_token;
public uint wrapper_token;
}
[StructLayout (LayoutKind.Sequential, Pack = 1)]
internal unsafe struct MTProtocolMap {
public uint* protocol_tokens;
public IntPtr* protocols;
}
/* Keep Delegates, Trampolines and InitializationOptions in sync with monotouch-glue.m */ /* Keep Delegates, Trampolines and InitializationOptions in sync with monotouch-glue.m */
internal struct Trampolines { internal struct Trampolines {
public IntPtr tramp; public IntPtr tramp;
@ -1315,6 +1334,17 @@ namespace ObjCRuntime {
if (type == null || !type.IsInterface) if (type == null || !type.IsInterface)
return null; return null;
// Check if the static registrar knows about this protocol
unsafe {
var map = options->RegistrationMap;
if (map != null) {
var token = Class.GetTokenReference (type);
var wrapper_token = xamarin_find_protocol_wrapper_type (token);
if (wrapper_token != INVALID_TOKEN_REF)
return (Type) Class.ResolveTokenReference (wrapper_token, 0x02000000 /* TypeDef */);
}
}
// need to look up the type from the ProtocolAttribute. // need to look up the type from the ProtocolAttribute.
var a = type.GetCustomAttributes (typeof (Foundation.ProtocolAttribute), false); var a = type.GetCustomAttributes (typeof (Foundation.ProtocolAttribute), false);
@ -1326,11 +1356,41 @@ namespace ObjCRuntime {
return attr.WrapperType; return attr.WrapperType;
} }
[DllImport ("__Internal")]
extern static uint xamarin_find_protocol_wrapper_type (uint token_ref);
public static IntPtr GetProtocol (string protocol) public static IntPtr GetProtocol (string protocol)
{ {
return Protocol.objc_getProtocol (protocol); return Protocol.objc_getProtocol (protocol);
} }
internal static IntPtr GetProtocolForType (Type type)
{
// Check if the static registrar knows about this protocol
unsafe {
var map = options->RegistrationMap;
if (map != null && map->protocol_count > 0) {
var token = Class.GetTokenReference (type);
var tokens = map->protocol_map.protocol_tokens;
for (int i = 0; i < map->protocol_count; i++) {
if (tokens [i] == token)
return map->protocol_map.protocols [i];
}
}
}
if (type.IsInterface) {
var pa = type.GetCustomAttribute<ProtocolAttribute> (false);
if (pa != null) {
var handle = Protocol.objc_getProtocol (pa.Name);
if (handle != IntPtr.Zero)
return handle;
}
}
throw new ArgumentException (string.Format ("'{0}' is an unknown protocol", type.FullName));
}
public static void ConnectMethod (Type type, MethodInfo method, Selector selector) public static void ConnectMethod (Type type, MethodInfo method, Selector selector)
{ {
if (selector == null) if (selector == null)

Просмотреть файл

@ -21,6 +21,41 @@ namespace Xamarin
[TestFixture] [TestFixture]
public class BundlerTests public class BundlerTests
{ {
[Test]
#if __MACOS__
[TestCase (Profile.macOSMobile)]
#else
[TestCase (Profile.iOS)]
#endif
public void RegisterProtocolOptimization (Profile profile)
{
using (var bundler = new BundlerTool ()) {
bundler.Profile = profile;
bundler.CreateTemporaryCacheDirectory ();
bundler.CreateTemporaryApp (profile);
bundler.Linker = LinkerOption.LinkAll;
bundler.Registrar = RegistrarOption.Static;
bundler.Optimize = new string [] { "register-protocols" };
bundler.AssertExecute ();
bundler.AssertWarningCount (0);
AssemblyDefinition ad = AssemblyDefinition.ReadAssembly (bundler.GetPlatformAssemblyInApp ());
var failures = new List<string> ();
foreach (var attrib in ad.MainModule.GetCustomAttributes ()) {
switch (attrib.AttributeType.Name) {
case "ProtocolAttribute":
case "ProtocolMemberAttribute":
case "AdoptsAttribute":
// Unfortunately the CustomAttribute doesn't know its owner, so we can't show that in the test failure message :(
failures.Add ($"Found an unexpected attribute: {attrib.AttributeType.FullName}");
break;
}
}
Assert.That (failures, Is.Empty, "all these attributes should have been linked away");
}
}
[Test] [Test]
#if __MACOS__ #if __MACOS__
[TestCase (Profile.macOSMobile)] [TestCase (Profile.macOSMobile)]

Просмотреть файл

@ -364,5 +364,15 @@ namespace Xamarin.Tests
return assembly; return assembly;
} }
// The directory where the assemblies are located in the built app
public abstract string GetAppAssembliesDirectory ();
// The path to the platform assembly in the built app.
public string GetPlatformAssemblyInApp ()
{
var asm = Path.GetFileName (Configuration.GetBaseLibrary (Profile));
return Path.Combine (GetAppAssembliesDirectory (), asm);
}
} }
} }

Просмотреть файл

@ -842,7 +842,7 @@ namespace Xamarin.MMP.Tests
"<LinkMode>Full</LinkMode>", "<LinkMode>Full</LinkMode>",
}; };
var rv = TI.TestUnifiedExecutable (test, shouldFail: false); var rv = TI.TestUnifiedExecutable (test, shouldFail: false);
rv.Messages.AssertWarning (132, $"Unknown optimization: '{opt}'. Valid optimizations are: remove-uithread-checks, dead-code-elimination, inline-isdirectbinding, inline-intptr-size, blockliteral-setupblock."); rv.Messages.AssertWarning (132, $"Unknown optimization: '{opt}'. Valid optimizations are: remove-uithread-checks, dead-code-elimination, inline-isdirectbinding, inline-intptr-size, blockliteral-setupblock, register-protocols.");
rv.Messages.AssertErrorCount (0); rv.Messages.AssertErrorCount (0);
}); });
} }

Просмотреть файл

@ -63,5 +63,10 @@ namespace Xamarin
Directory.CreateDirectory (app); Directory.CreateDirectory (app);
RootAssembly = CompileTestAppExecutable (OutputPath, code, extraArg, profile, appName, extraCode, usings, use_csc); RootAssembly = CompileTestAppExecutable (OutputPath, code, extraArg, profile, appName, extraCode, usings, use_csc);
} }
public override string GetAppAssembliesDirectory()
{
return Path.Combine (OutputPath, ApplicationName + ".app", "Contents", "MonoBundle");
}
} }
} }

Просмотреть файл

@ -83,7 +83,9 @@ namespace MonoTouchFixtures.Foundation {
{ {
Assert.Null (NSData.FromFile ("does not exists"), "unexisting"); Assert.Null (NSData.FromFile ("does not exists"), "unexisting");
#if MONOMAC // Info.Plist isn't there to load from the same location on mac #if MONOMAC // Info.Plist isn't there to load from the same location on mac
#if !LINKALL
Assert.NotNull (NSData.FromFile (NSBundle.MainBundle.PathForResource ("runtime-options", "plist")), "runtime-options.plist"); Assert.NotNull (NSData.FromFile (NSBundle.MainBundle.PathForResource ("runtime-options", "plist")), "runtime-options.plist");
#endif
#else #else
Assert.NotNull (NSData.FromFile ("Info.plist"), "Info.plist"); Assert.NotNull (NSData.FromFile ("Info.plist"), "Info.plist");
#endif #endif

Просмотреть файл

@ -0,0 +1,50 @@
//
// Unit tests for Protocol
//
// Authors:
// Rolf Bjarne Kvinge <rolf@xamarin.com>
//
// Copyright 2018 Microsoft Inc. All rights reserved.
//
#if XAMCORE_2_0
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;
using Foundation;
using ObjCRuntime;
using NUnit.Framework;
namespace MonoTouchFixtures.ObjCRuntime
{
[TestFixture]
[Preserve (AllMembers = true)]
public class ProtocolTest
{
[Test]
public void Ctors ()
{
var data = new [] {
new { Type = typeof (INSObjectProtocol), Name = "NSObject" }, // protocol name doesn't match at all
new { Type = typeof (INSUrlSessionDownloadDelegate), Name = "NSURLSessionDownloadDelegate" }, // different casing in native
new { Type = typeof (global::CloudKit.ICKRecordValue), Name = "CKRecordValue" }, // protocol name is the same in native and managed
};
foreach (var d in data) {
Assert.AreNotEqual (IntPtr.Zero, new Protocol (d.Type).Handle, $"{d.Name} type");
Assert.AreNotEqual (IntPtr.Zero, new Protocol (d.Name).Handle, $"{d.Name} string");
Assert.AreEqual (d.Name, new Protocol (d.Name).Name, $"{d.Name} name");
Assert.AreEqual (d.Name, new Protocol (d.Type).Name, $"{d.Name} type name");
Assert.AreEqual (d.Name, new Protocol (new Protocol (d.Name).Handle).Name, $"{d.Name} IntPtr name");
Assert.AreEqual (d.Name, new Protocol (Protocol.GetHandle (d.Name)).Name, $"{d.Name} GetHandle name");
}
}
}
}
#endif // XAMCORE_2_0

Просмотреть файл

@ -1639,20 +1639,21 @@ public class TestApp {
mtouch.Linker = MTouchLinker.LinkSdk; mtouch.Linker = MTouchLinker.LinkSdk;
mtouch.Optimize = new string [] { "foo" }; mtouch.Optimize = new string [] { "foo" };
mtouch.AssertExecute (MTouchAction.BuildSim, "build"); mtouch.AssertExecute (MTouchAction.BuildSim, "build");
mtouch.AssertWarning (132, "Unknown optimization: 'foo'. Valid optimizations are: remove-uithread-checks, dead-code-elimination, inline-isdirectbinding, inline-intptr-size, inline-runtime-arch, blockliteral-setupblock."); mtouch.AssertWarning (132, "Unknown optimization: 'foo'. Valid optimizations are: remove-uithread-checks, dead-code-elimination, inline-isdirectbinding, inline-intptr-size, inline-runtime-arch, blockliteral-setupblock, register-protocols.");
} }
} }
[Test] [Test]
[TestCase ("all")] [TestCase ("all")]
[TestCase ("-all")] [TestCase ("-all")]
[TestCase ("remove-uithread-checks,dead-code-elimination,inline-isdirectbinding,inline-intptr-size,inline-runtime-arch")] [TestCase ("remove-uithread-checks,dead-code-elimination,inline-isdirectbinding,inline-intptr-size,inline-runtime-arch,register-protocols")]
public void Optimizations (string opt) public void Optimizations (string opt)
{ {
using (var mtouch = new MTouchTool ()) { using (var mtouch = new MTouchTool ()) {
mtouch.CreateTemporaryApp (); mtouch.CreateTemporaryApp ();
mtouch.CreateTemporaryCacheDirectory (); mtouch.CreateTemporaryCacheDirectory ();
mtouch.Linker = MTouchLinker.LinkSdk; mtouch.Linker = MTouchLinker.LinkSdk;
mtouch.Registrar = MTouchRegistrar.Static;
mtouch.Optimize = new string [] { opt }; mtouch.Optimize = new string [] { opt };
mtouch.AssertExecute (MTouchAction.BuildSim, "build"); mtouch.AssertExecute (MTouchAction.BuildSim, "build");
mtouch.AssertNoWarnings (); mtouch.AssertNoWarnings ();
@ -3266,7 +3267,8 @@ public partial class NotificationService : UNNotificationServiceExtension
mtouch.AssertWarning (2003, "Option '--optimize=inline-intptr-size' will be ignored since linking is disabled"); mtouch.AssertWarning (2003, "Option '--optimize=inline-intptr-size' will be ignored since linking is disabled");
mtouch.AssertWarning (2003, "Option '--optimize=inline-runtime-arch' will be ignored since linking is disabled"); mtouch.AssertWarning (2003, "Option '--optimize=inline-runtime-arch' will be ignored since linking is disabled");
mtouch.AssertWarning (2003, "Option '--optimize=blockliteral-setupblock' will be ignored since linking is disabled"); mtouch.AssertWarning (2003, "Option '--optimize=blockliteral-setupblock' will be ignored since linking is disabled");
mtouch.AssertWarningCount (6); mtouch.AssertWarning (2003, "Option '--optimize=register-protocols' will be ignored since the static registrar is not enabled");
mtouch.AssertWarningCount (7);
} }
using (var mtouch = new MTouchTool ()) { using (var mtouch = new MTouchTool ()) {

Просмотреть файл

@ -714,5 +714,10 @@ public partial class NotificationController : WKUserNotificationInterfaceControl
protected override string MessagePrefix { protected override string MessagePrefix {
get { return "MT"; } get { return "MT"; }
} }
public override string GetAppAssembliesDirectory ()
{
return AppPath;
}
} }
} }

Просмотреть файл

@ -692,7 +692,14 @@ class X : ReplayKit.RPBroadcastControllerDelegate
} }
} }
"; ";
Verify (R.Static, code, true); using (var mtouch = new MTouchTool ()) {
mtouch.CreateTemporaryCacheDirectory ();
mtouch.CreateTemporaryApp (extraCode: code, usings: "using System; using Foundation;", extraArg: "/debug:full");
mtouch.Linker = MTouchLinker.DontLink;
mtouch.Registrar = MTouchRegistrar.Static;
mtouch.AssertExecute ();
mtouch.AssertNoWarnings ();
}
} }
[Test] [Test]
@ -919,7 +926,14 @@ class H : G {
} }
"; ";
Verify (R.Static, code, true); using (var mtouch = new MTouchTool ()) {
mtouch.CreateTemporaryCacheDirectory ();
mtouch.CreateTemporaryApp (extraCode: code, usings: "using Foundation;", extraArg: "/debug:full");
mtouch.Linker = MTouchLinker.DontLink;
mtouch.Registrar = MTouchRegistrar.Static;
mtouch.AssertExecute ();
mtouch.AssertNoWarnings ();
}
} }
void Verify (R registrars, string code, bool success, params string [] expected_messages) void Verify (R registrars, string code, bool success, params string [] expected_messages)
@ -1047,7 +1061,14 @@ class Generic3<T> : NSObject where T: System.IConvertible {}
"; ";
// and the lack of warnings/errors in the new static registrar. // and the lack of warnings/errors in the new static registrar.
Verify (R.Static, code, true); using (var mtouch = new MTouchTool ()) {
mtouch.CreateTemporaryCacheDirectory ();
mtouch.CreateTemporaryApp (extraCode: code, usings: "using Foundation; using UIKit;", extraArg: "/debug:full");
mtouch.Linker = MTouchLinker.DontLink;
mtouch.Registrar = MTouchRegistrar.Static;
mtouch.AssertExecute ();
mtouch.AssertNoWarnings ();
}
} }
[Test] [Test]
@ -1244,10 +1265,17 @@ class GenericMethodClass : NSObject {
} }
"; ";
Verify (R.Static, str1, false, using (var mtouch = new MTouchTool ()) {
".*Test.cs.*: error MT4113: The registrar found a generic method: 'GenericMethodClass.GenericMethod(T)'. Exporting generic methods is not supported, and will lead to random behavior and/or crashes", mtouch.CreateTemporaryCacheDirectory ();
".*Test.cs.*: error MT4113: The registrar found a generic method: 'GenericMethodClass.Foo(T)'. Exporting generic methods is not supported, and will lead to random behavior and/or crashes" mtouch.CreateTemporaryApp (extraCode: str1, usings: "using Foundation;", extraArg: "/debug:full");
); mtouch.Linker = MTouchLinker.DontLink;
mtouch.Registrar = MTouchRegistrar.Static;
mtouch.AssertExecuteFailure ();
mtouch.AssertError (4113, "The registrar found a generic method: 'GenericMethodClass.Foo(T)'. Exporting generic methods is not supported, and will lead to random behavior and/or crashes", "testApp.cs", 6);
mtouch.AssertError (4113, "The registrar found a generic method: 'GenericMethodClass.GenericMethod(T)'. Exporting generic methods is not supported, and will lead to random behavior and/or crashes", "testApp.cs", 8);
mtouch.AssertErrorCount (2);
mtouch.AssertNoWarnings ();
}
} }
[Test] [Test]
@ -1259,7 +1287,14 @@ class GenericMethodClass : NSObject {
public virtual void Foo (System.Action<string> func) {} public virtual void Foo (System.Action<string> func) {}
} }
"; ";
Verify (R.Static, code, true); using (var mtouch = new MTouchTool ()) {
mtouch.CreateTemporaryCacheDirectory ();
mtouch.CreateTemporaryApp (extraCode: code, usings: "using Foundation;", extraArg: "/debug:full");
mtouch.Linker = MTouchLinker.DontLink;
mtouch.Registrar = MTouchRegistrar.Static;
mtouch.AssertExecute ();
mtouch.AssertNoWarnings ();
}
} }
[Test] [Test]
@ -1369,7 +1404,14 @@ class CTP4 : CTP3 {
public override bool ConformsToProtocol (IntPtr protocol) { return base.ConformsToProtocol (protocol); } public override bool ConformsToProtocol (IntPtr protocol) { return base.ConformsToProtocol (protocol); }
} }
"; ";
Verify (R.Static, code, true); using (var mtouch = new MTouchTool ()) {
mtouch.CreateTemporaryCacheDirectory ();
mtouch.CreateTemporaryApp (extraCode: code, usings: "using System; using Foundation;");
mtouch.Registrar = MTouchRegistrar.Static;
mtouch.Linker = MTouchLinker.DontLink;
mtouch.AssertExecute ();
mtouch.AssertNoWarnings ();
}
} }
#region Helper functions #region Helper functions

Просмотреть файл

@ -151,8 +151,11 @@ namespace xharness
{ {
public string Variation; public string Variation;
public string MTouchExtraArgs; public string MTouchExtraArgs;
public string MonoBundlingExtraArgs; // mmp
public bool Debug; public bool Debug;
public bool Profiling; public bool Profiling;
public string LinkMode;
public string Defines;
} }
IEnumerable<TestData> GetTestData (RunTestTask test) IEnumerable<TestData> GetTestData (RunTestTask test)
@ -170,12 +173,28 @@ namespace xharness
yield return new TestData { Variation = "Release", MTouchExtraArgs = "", Debug = false, Profiling = false }; yield return new TestData { Variation = "Release", MTouchExtraArgs = "", Debug = false, Profiling = false };
yield return new TestData { Variation = "AssemblyBuildTarget: SDK framework (release)", MTouchExtraArgs = "--assembly-build-target=@sdk=framework=Xamarin.Sdk --assembly-build-target=@all=staticobject", Debug = false, Profiling = false }; yield return new TestData { Variation = "AssemblyBuildTarget: SDK framework (release)", MTouchExtraArgs = "--assembly-build-target=@sdk=framework=Xamarin.Sdk --assembly-build-target=@all=staticobject", Debug = false, Profiling = false };
switch (test.TestName) {
case "monotouch-test":
yield return new TestData { Variation = "Release (all optimizations)", MTouchExtraArgs = "--registrar:static --optimize:all", Debug = false, Profiling = false };
break;
}
break; break;
case "iPhoneSimulator": case "iPhoneSimulator":
switch (test.TestName) { switch (test.TestName) {
case "monotouch-test": case "monotouch-test":
// The default is to run monotouch-test with the dynamic registrar (in the simulator), so that's already covered // The default is to run monotouch-test with the dynamic registrar (in the simulator), so that's already covered
yield return new TestData { Variation = "Debug (static registrar)", MTouchExtraArgs = "--registrar:static", Debug = true, Profiling = false }; yield return new TestData { Variation = "Debug (static registrar)", MTouchExtraArgs = "--registrar:static", Debug = true, Profiling = false };
yield return new TestData { Variation = "Release (all optimizations)", MTouchExtraArgs = "--registrar:static --optimize:all", Debug = false, Profiling = false, LinkMode = "Full" };
break;
}
break;
case "AnyCPU":
case "x86":
switch (test.TestName) {
case "xammac tests":
if (test.ProjectConfiguration == "Release")
yield return new TestData { Variation = "Release (all optimizations)", MonoBundlingExtraArgs = "--registrar:static --optimize:all", Debug = false, LinkMode = "Full", Defines = "LINKALL" };
break; break;
} }
break; break;
@ -196,9 +215,12 @@ namespace xharness
foreach (var test_data in GetTestData (task)) { foreach (var test_data in GetTestData (task)) {
var variation = test_data.Variation; var variation = test_data.Variation;
var mtouch_extra_args = test_data.MTouchExtraArgs; var mtouch_extra_args = test_data.MTouchExtraArgs;
var bundling_extra_args = test_data.MonoBundlingExtraArgs;
var configuration = test_data.Debug ? task.ProjectConfiguration : task.ProjectConfiguration.Replace ("Debug", "Release"); var configuration = test_data.Debug ? task.ProjectConfiguration : task.ProjectConfiguration.Replace ("Debug", "Release");
var debug = test_data.Debug; var debug = test_data.Debug;
var profiling = test_data.Profiling; var profiling = test_data.Profiling;
var link_mode = test_data.LinkMode;
var defines = test_data.Defines;
var clone = task.TestProject.Clone (); var clone = task.TestProject.Clone ();
var clone_task = Task.Run (async () => { var clone_task = Task.Run (async () => {
@ -206,8 +228,26 @@ namespace xharness
await clone.CreateCopyAsync (task); await clone.CreateCopyAsync (task);
if (!string.IsNullOrEmpty (mtouch_extra_args)) if (!string.IsNullOrEmpty (mtouch_extra_args))
clone.Xml.AddExtraMtouchArgs (mtouch_extra_args, task.ProjectPlatform, configuration); clone.Xml.AddExtraMtouchArgs (mtouch_extra_args, task.ProjectPlatform, configuration);
clone.Xml.SetNode ("MTouchProfiling", profiling ? "True" : "False", task.ProjectPlatform, configuration); if (!string.IsNullOrEmpty (bundling_extra_args))
if (!debug) clone.Xml.AddMonoBundlingExtraArgs (bundling_extra_args, task.ProjectPlatform, configuration);
if (!string.IsNullOrEmpty (link_mode))
clone.Xml.SetNode ("LinkMode", link_mode, task.ProjectPlatform, configuration);
if (!string.IsNullOrEmpty (defines))
clone.Xml.AddAdditionalDefines (defines, task.ProjectPlatform, configuration);
var isMac = false;
switch (task.Platform) {
case TestPlatform.Mac:
case TestPlatform.Mac_Classic:
case TestPlatform.Mac_Unified:
case TestPlatform.Mac_Unified32:
case TestPlatform.Mac_UnifiedXM45:
case TestPlatform.Mac_UnifiedXM45_32:
isMac = true;
break;
}
clone.Xml.SetNode (isMac ? "Profiling" : "MTouchProfiling", profiling ? "True" : "False", task.ProjectPlatform, configuration);
if (!debug && !isMac)
clone.Xml.SetMtouchUseLlvm (true, task.ProjectPlatform, configuration); clone.Xml.SetMtouchUseLlvm (true, task.ProjectPlatform, configuration);
clone.Xml.Save (clone.Path); clone.Xml.Save (clone.Path);
}); });
@ -611,6 +651,7 @@ namespace xharness
build.SpecifyPlatform = false; build.SpecifyPlatform = false;
build.SpecifyConfiguration = build.ProjectConfiguration != "Debug"; build.SpecifyConfiguration = build.ProjectConfiguration != "Debug";
RunTestTask exec; RunTestTask exec;
IEnumerable<RunTestTask> execs;
if (project.IsNUnitProject) { if (project.IsNUnitProject) {
var dll = Path.Combine (Path.GetDirectoryName (build.TestProject.Path), project.Xml.GetOutputAssemblyPath (build.ProjectPlatform, build.ProjectConfiguration).Replace ('\\', '/')); var dll = Path.Combine (Path.GetDirectoryName (build.TestProject.Path), project.Xml.GetOutputAssemblyPath (build.ProjectPlatform, build.ProjectConfiguration).Replace ('\\', '/'));
exec = new NUnitExecuteTask (build) { exec = new NUnitExecuteTask (build) {
@ -622,6 +663,7 @@ namespace xharness
TestName = project.Name, TestName = project.Name,
Timeout = TimeSpan.FromMinutes (120), Timeout = TimeSpan.FromMinutes (120),
}; };
execs = new [] { exec };
} else { } else {
exec = new MacExecuteTask (build) { exec = new MacExecuteTask (build) {
Ignored = ignored || !IncludeClassicMac, Ignored = ignored || !IncludeClassicMac,
@ -629,16 +671,19 @@ namespace xharness
TestName = project.Name, TestName = project.Name,
IsUnitTest = true, IsUnitTest = true,
}; };
execs = CreateTestVariations (new [] { exec }, (buildTask, test) => new MacExecuteTask (buildTask));
} }
exec.Variation = configurations.Length > 1 ? config : project.TargetFrameworkFlavor.ToString (); exec.Variation = configurations.Length > 1 ? config : project.TargetFrameworkFlavor.ToString ();
Tasks.Add (exec);
if (project.GenerateVariations) { Tasks.AddRange (execs);
Tasks.Add (CloneExecuteTask (exec, project, TestPlatform.Mac_Unified, "-unified", ignored)); foreach (var e in execs) {
Tasks.Add (CloneExecuteTask (exec, project, TestPlatform.Mac_Unified32, "-unified-32", ignored)); if (project.GenerateVariations) {
if (project.GenerateFull) { Tasks.Add (CloneExecuteTask (e, project, TestPlatform.Mac_Unified, "-unified", ignored));
Tasks.Add (CloneExecuteTask (exec, project, TestPlatform.Mac_UnifiedXM45, "-unifiedXM45", ignored)); Tasks.Add (CloneExecuteTask (e, project, TestPlatform.Mac_Unified32, "-unified-32", ignored));
Tasks.Add (CloneExecuteTask (exec, project, TestPlatform.Mac_UnifiedXM45_32, "-unifiedXM45-32", ignored)); if (project.GenerateFull) {
Tasks.Add (CloneExecuteTask (e, project, TestPlatform.Mac_UnifiedXM45, "-unifiedXM45", ignored));
Tasks.Add (CloneExecuteTask (e, project, TestPlatform.Mac_UnifiedXM45_32, "-unifiedXM45-32", ignored));
}
} }
} }
} }

Просмотреть файл

@ -324,9 +324,19 @@ namespace xharness
public static void AddExtraMtouchArgs (this XmlDocument csproj, string value, string platform, string configuration) public static void AddExtraMtouchArgs (this XmlDocument csproj, string value, string platform, string configuration)
{ {
var mtouchExtraArgs = csproj.SelectNodes ("//*[local-name() = 'MtouchExtraArgs']"); AddToNode (csproj, "MTouchExtraArgs", value, platform, configuration);
}
public static void AddMonoBundlingExtraArgs (this XmlDocument csproj, string value, string platform, string configuration)
{
AddToNode (csproj, "MonoBundlingExtraArgs", value, platform, configuration);
}
public static void AddToNode (this XmlDocument csproj, string node, string value, string platform, string configuration)
{
var nodes = csproj.SelectNodes ($"//*[local-name() = '{node}']");
var found = false; var found = false;
foreach (XmlNode mea in mtouchExtraArgs) { foreach (XmlNode mea in nodes) {
if (!IsNodeApplicable (mea, platform, configuration)) if (!IsNodeApplicable (mea, platform, configuration))
continue; continue;
@ -337,18 +347,17 @@ namespace xharness
if (found) if (found)
return; return;
// Not all projects have a MtouchExtraArgs node, so create one of none was found. // The project might not have this node, so create one of none was found.
var propertyGroups = csproj.SelectNodes ("//*[local-name() = 'PropertyGroup' and @Condition]"); var propertyGroups = csproj.SelectNodes ("//*[local-name() = 'PropertyGroup' and @Condition]");
foreach (XmlNode pg in propertyGroups) { foreach (XmlNode pg in propertyGroups) {
if (!EvaluateCondition (pg, platform, configuration)) if (!EvaluateCondition (pg, platform, configuration))
continue; continue;
var mea = csproj.CreateElement ("MtouchExtraArgs", MSBuild_Namespace); var mea = csproj.CreateElement (node, MSBuild_Namespace);
mea.InnerText = value; mea.InnerText = value;
pg.AppendChild (mea); pg.AppendChild (mea);
} }
} }
public static string GetExtraMtouchArgs (this XmlDocument csproj, string platform, string configuration) public static string GetExtraMtouchArgs (this XmlDocument csproj, string platform, string configuration)
{ {
var mtouchExtraArgs = csproj.SelectNodes ("//*[local-name() = 'MtouchExtraArgs']"); var mtouchExtraArgs = csproj.SelectNodes ("//*[local-name() = 'MtouchExtraArgs']");

Просмотреть файл

@ -1,4 +1,6 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Mono.Cecil; using Mono.Cecil;
using Mono.Linker; using Mono.Linker;
@ -25,6 +27,20 @@ namespace Xamarin.Tuner
// true/false = corresponding constant value // true/false = corresponding constant value
Dictionary<TypeDefinition, bool?> isdirectbinding_value; Dictionary<TypeDefinition, bool?> isdirectbinding_value;
// Store interfaces the linker has linked away so that the static registrar can access them.
public Dictionary<TypeDefinition, List<TypeDefinition>> ProtocolImplementations { get; private set; } = new Dictionary<TypeDefinition, List<TypeDefinition>> ();
// Store types the linker has linked away so that the static registrar can access them.
Dictionary<string, LinkedAwayTypeReference> LinkedAwayTypes = new Dictionary<string, LinkedAwayTypeReference> ();
// The linked away TypeDefinitions lacks some information (it can't even find itself in the LinkedAwayTypes dictionary)
// so we need a second dictionary
Dictionary<TypeDefinition, LinkedAwayTypeReference> LinkedAwayTypeMap = new Dictionary<TypeDefinition, LinkedAwayTypeReference> ();
public Application App {
get {
return Target.App;
}
}
public HashSet<TypeDefinition> CachedIsNSObject { public HashSet<TypeDefinition> CachedIsNSObject {
get { return cached_isnsobject; } get { return cached_isnsobject; }
set { cached_isnsobject = value; } set { cached_isnsobject = value; }
@ -109,6 +125,67 @@ namespace Xamarin.Tuner
StoreCustomAttribute (provider, attribute, attribute.AttributeType.Name); StoreCustomAttribute (provider, attribute, attribute.AttributeType.Name);
} }
public void StoreProtocolMethods (TypeDefinition type)
{
var attribs = Annotations.GetCustomAnnotations ("ProtocolMethods");
object value;
if (!attribs.TryGetValue (type, out value))
attribs [type] = type.Methods.ToArray (); // Make a copy of the collection, since the linker may remove methods from it.
}
public IList<MethodDefinition> GetProtocolMethods (TypeDefinition type)
{
var attribs = Annotations.GetCustomAnnotations ("ProtocolMethods");
object value;
if (attribs.TryGetValue (type, out value))
return (MethodDefinition []) value;
return null;
}
public void AddLinkedAwayType (TypeDefinition td)
{
var latr = new LinkedAwayTypeReference (td);
LinkedAwayTypes.Add (td.Module.Assembly.Name.Name + ": " + td.FullName, new LinkedAwayTypeReference (td));
LinkedAwayTypeMap.Add (td, latr);
}
// Returns a TypeDefinition for a type that was linked away.
// The Module property is null for the returned TypeDefinition (unfortunately TypeDefinition is
// sealed, so I can't provide a custom TypeDefinition subclass), so we need to return
// the module as an out parameter instead.
public TypeDefinition GetLinkedAwayType (TypeReference tr, out ModuleDefinition module)
{
module = null;
if (tr is TypeDefinition td) {
// We might get called again for a TypeDefinition we already returned,
// in which case tr.Scope will be null, so we can't use the code below.
// Instead look in our second dictionary, of TypeDefinition -> LinkedAwayTypeReference
// to find the module.
if (LinkedAwayTypeMap.TryGetValue (td, out var latd))
module = latd.Module;
return td;
}
var name = tr.Scope.Name;
switch (tr.Scope.MetadataScopeType) {
case MetadataScopeType.ModuleDefinition:
var md = (ModuleDefinition) tr.Scope;
name = md.Assembly.Name.Name;
break;
default:
name = tr.Scope.Name;
break;
}
if (LinkedAwayTypes.TryGetValue (name + ": " + tr.FullName, out var latr)) {
module = latr.Module;
return latr.Resolve ();
}
return null;
}
class AttributeStorage : ICustomAttribute class AttributeStorage : ICustomAttribute
{ {
public CustomAttribute Attribute; public CustomAttribute Attribute;
@ -122,5 +199,39 @@ namespace Xamarin.Tuner
public Collection<CustomAttributeNamedArgument> Properties => Attribute.Properties; public Collection<CustomAttributeNamedArgument> Properties => Attribute.Properties;
public Collection<CustomAttributeArgument> ConstructorArguments => Attribute.ConstructorArguments; public Collection<CustomAttributeArgument> ConstructorArguments => Attribute.ConstructorArguments;
} }
class LinkedAwayTypeReference : TypeReference
{
// When a type is linked away, its Module and Scope properties
// return null.
// This class keeps those values around.
TypeDefinition type;
ModuleDefinition module;
IMetadataScope scope;
public LinkedAwayTypeReference (TypeDefinition type)
: base (type.Namespace, type.Name)
{
this.type = type;
this.module = type.Module;
this.scope = type.Scope;
}
public override TypeDefinition Resolve ()
{
return type;
}
public override ModuleDefinition Module {
get {
return module;
}
}
public override IMetadataScope Scope {
get { return scope; }
set { scope = value; }
}
}
} }
} }

Просмотреть файл

@ -139,7 +139,8 @@ namespace Xamarin.Bundler {
" inline-runtime-arch: By default always enabled (requires the linker). Inlines calls to ObjCRuntime.Runtime.Arch to load a constant value. Makes the app smaller, and slightly faster at runtime.\n" + " inline-runtime-arch: By default always enabled (requires the linker). Inlines calls to ObjCRuntime.Runtime.Arch to load a constant value. Makes the app smaller, and slightly faster at runtime.\n" +
#endif #endif
" blockliteral-setupblock: By default enabled when using the static registrar. Optimizes calls to BlockLiteral.SetupBlock to avoid having to calculate the block signature at runtime.\n" + " blockliteral-setupblock: By default enabled when using the static registrar. Optimizes calls to BlockLiteral.SetupBlock to avoid having to calculate the block signature at runtime.\n" +
" inline-intptr-size: By default enabled for builds that target a single architecture (requires the linker). Inlines calls to IntPtr.Size to load a constant value. Makes the app smaller, and slightly faster at runtime.\n", " inline-intptr-size: By default enabled for builds that target a single architecture (requires the linker). Inlines calls to IntPtr.Size to load a constant value. Makes the app smaller, and slightly faster at runtime.\n" +
" register-protocols: Remove unneeded metadata for protocol support. Makes the app smaller and reduces memory requirements.\n",
(v) => { (v) => {
app.Optimizations.Parse (v); app.Optimizations.Parse (v);
}); });

Просмотреть файл

@ -16,6 +16,7 @@ namespace Xamarin.Bundler
"", // dummy value to make indices match up between XM and XI "", // dummy value to make indices match up between XM and XI
#endif #endif
"blockliteral-setupblock", "blockliteral-setupblock",
"register-protocols",
}; };
bool? [] values; bool? [] values;
@ -46,6 +47,10 @@ namespace Xamarin.Bundler
get { return values [5]; } get { return values [5]; }
set { values [5] = value; } set { values [5] = value; }
} }
public bool? RegisterProtocols {
get { return values [6]; }
set { values [6] = value; }
}
public Optimizations () public Optimizations ()
{ {
@ -54,14 +59,25 @@ namespace Xamarin.Bundler
public void Initialize (Application app) public void Initialize (Application app)
{ {
// warn if the user asked to optimize something when the linker is not enabled // warn if the user asked to optimize something when the optimization can't be applied
if (app.LinkMode == LinkMode.None) { for (int i = 0; i < values.Length; i++) {
for (int i = 0; i < values.Length; i++) { if (!values [i].HasValue)
if (!values [i].HasValue) continue;
switch (i) {
case 6:
if (app.Registrar != RegistrarMode.Static) {
ErrorHelper.Warning (2003, $"Option '--optimize={(values [i].Value ? "" : "-")}{opt_names [i]}' will be ignored since the static registrar is not enabled");
values [i] = false;
continue; continue;
ErrorHelper.Warning (2003, $"Option '--optimize={(values [i].Value ? "" : "-")}{opt_names [i]}' will be ignored since linking is disabled"); }
goto default; // also requires the linker
default:
if (app.LinkMode == LinkMode.None) {
ErrorHelper.Warning (2003, $"Option '--optimize={(values [i].Value ? "" : "-")}{opt_names [i]}' will be ignored since linking is disabled");
values [i] = false;
}
break;
} }
return;
} }
// by default we keep the code to ensure we're executing on the UI thread (for UI code) for debug builds // by default we keep the code to ensure we're executing on the UI thread (for UI code) for debug builds
@ -103,6 +119,17 @@ namespace Xamarin.Bundler
#endif #endif
} }
// We will register protocols if the static registrar is enabled
if (!RegisterProtocols.HasValue) {
#if MONOTOUCH
RegisterProtocols = app.Registrar == RegistrarMode.Static;
#else
RegisterProtocols = false;
#endif
} else if (app.Registrar != RegistrarMode.Static && RegisterProtocols == true) {
RegisterProtocols = false; // we've already shown a warning for this.
}
if (Driver.Verbosity > 3) if (Driver.Verbosity > 3)
Driver.Log (4, "Enabled optimizations: {0}", string.Join (", ", values.Select ((v, idx) => v == true ? opt_names [idx] : string.Empty).Where ((v) => !string.IsNullOrEmpty (v)))); Driver.Log (4, "Enabled optimizations: {0}", string.Join (", ", values.Select ((v, idx) => v == true ? opt_names [idx] : string.Empty).Where ((v) => !string.IsNullOrEmpty (v))));
} }

Просмотреть файл

@ -215,29 +215,6 @@ namespace Registrar {
} }
} }
public static bool TryGetAttributeImpl (ICustomAttributeProvider provider, string @namespace, string attributeName, out CustomAttribute attribute)
{
attribute = null;
if (!provider.HasCustomAttributes)
return false;
foreach (CustomAttribute custom_attribute in provider.CustomAttributes) {
if (!custom_attribute.Constructor.DeclaringType.Is (@namespace, attributeName))
continue;
attribute = custom_attribute;
return true;
}
return false;
}
public static bool HasAttribute (ICustomAttributeProvider provider, string @namespace, string attributeName)
{
CustomAttribute attrib;
return TryGetAttributeImpl (provider, @namespace, attributeName, out attrib);
}
public static bool TypeMatch (IModifierType a, IModifierType b) public static bool TypeMatch (IModifierType a, IModifierType b)
{ {
if (!TypeMatch (a.ModifierType, b.ModifierType)) if (!TypeMatch (a.ModifierType, b.ModifierType))
@ -314,7 +291,7 @@ namespace Registrar {
return true; return true;
} }
static void CollectInterfaces (ref List<TypeDefinition> ifaces, TypeDefinition type) void CollectInterfaces (ref List<TypeDefinition> ifaces, TypeDefinition type)
{ {
if (type == null) if (type == null)
return; return;
@ -342,7 +319,7 @@ namespace Registrar {
} }
} }
public static Dictionary<MethodDefinition, List<MethodDefinition>> PrepareInterfaceMethodMapping (TypeReference type) public Dictionary<MethodDefinition, List<MethodDefinition>> PrepareInterfaceMethodMapping (TypeReference type)
{ {
TypeDefinition td = type.Resolve (); TypeDefinition td = type.Resolve ();
List<TypeDefinition> ifaces = null; List<TypeDefinition> ifaces = null;
@ -356,6 +333,12 @@ namespace Registrar {
iface_methods = new List<MethodDefinition> (); iface_methods = new List<MethodDefinition> ();
foreach (var iface in ifaces) { foreach (var iface in ifaces) {
var storedMethods = LinkContext?.GetProtocolMethods (iface.Resolve ());
if (storedMethods?.Count > 0) {
foreach (var imethod in storedMethods)
if (!iface_methods.Contains (imethod))
iface_methods.Add (imethod);
}
if (!iface.HasMethods) if (!iface.HasMethods)
continue; continue;
@ -423,7 +406,7 @@ namespace Registrar {
return null; return null;
} }
public static string ToObjCType (TypeReference type) public string ToObjCType (TypeReference type)
{ {
var definition = type as TypeDefinition; var definition = type as TypeDefinition;
if (definition != null) if (definition != null)
@ -439,7 +422,7 @@ namespace Registrar {
return "void *"; return "void *";
} }
public static string ToObjCType (TypeDefinition type) public string ToObjCType (TypeDefinition type)
{ {
switch (type.FullName) { switch (type.FullName) {
case "System.IntPtr": return "void *"; case "System.IntPtr": return "void *";
@ -489,7 +472,25 @@ namespace Registrar {
return false; return false;
} }
public static bool IsNativeObject (TypeReference tr) TypeDefinition ResolveType (TypeReference tr)
{
// The static registrar might sometimes deal with types that have been linked away
// It's not always possible to call .Resolve () on types that have been linked away,
// it might result in a NotSupportedException, or just a null value, so here we
// manually replicate some resolution logic to try to avoid those NotSupportedExceptions.
// The static registrar calls .Resolve () in a lot of places; we'll only call
// this method if there's an actual need.
if (tr is ArrayType arrayType) {
return arrayType.ElementType.Resolve ();
} else {
var td = tr.Resolve ();
if (td == null)
td = LinkContext?.GetLinkedAwayType (tr, out _);
return td;
}
}
public bool IsNativeObject (TypeReference tr)
{ {
var gp = tr as GenericParameter; var gp = tr as GenericParameter;
if (gp != null) { if (gp != null) {
@ -502,7 +503,8 @@ namespace Registrar {
return false; return false;
} }
var type = tr.Resolve (); var type = ResolveType (tr);
while (type != null) { while (type != null) {
if (type.HasInterfaces) { if (type.HasInterfaces) {
foreach (var iface in type.Interfaces) foreach (var iface in type.Interfaces)
@ -541,6 +543,52 @@ namespace Registrar {
} }
} }
// Look for linked away attributes as well as attributes on the attribute provider.
IEnumerable<ICustomAttribute> GetCustomAttributes (ICustomAttributeProvider provider, string @namespace, string name, bool inherits = false)
{
var dict = LinkContext?.Annotations?.GetCustomAnnotations (name);
object annotations = null;
if (dict?.TryGetValue (provider, out annotations) == true) {
var attributes = (IEnumerable<ICustomAttribute>) annotations;
foreach (var attrib in attributes) {
if (IsAttributeMatch (attrib, @namespace, name, inherits))
yield return attrib;
}
}
if (provider.HasCustomAttributes) {
foreach (var attrib in provider.CustomAttributes) {
if (IsAttributeMatch (attrib, @namespace, name, inherits))
yield return attrib;
}
}
}
public bool TryGetAttribute (ICustomAttributeProvider provider, string @namespace, string attributeName, out ICustomAttribute attribute)
{
attribute = null;
foreach (var custom_attribute in GetCustomAttributes (provider, @namespace, attributeName)) {
attribute = custom_attribute;
return true;
}
return false;
}
public bool HasAttribute (ICustomAttributeProvider provider, string @namespace, string name, bool inherits = false)
{
return GetCustomAttributes (provider, @namespace, name, inherits).Any ();
}
bool IsAttributeMatch (ICustomAttribute attribute, string @namespace, string name, bool inherits)
{
if (inherits)
return attribute.AttributeType.Inherits (@namespace, name);
return attribute.AttributeType.Is (@namespace, name);
}
void Init (Application app) void Init (Application app)
{ {
this.App = app; this.App = app;
@ -657,15 +705,13 @@ namespace Registrar {
protected override bool HasReleaseAttribute (MethodDefinition method) protected override bool HasReleaseAttribute (MethodDefinition method)
{ {
CustomAttribute attrib;
method = GetBaseMethodInTypeHierarchy (method); method = GetBaseMethodInTypeHierarchy (method);
return TryGetAttributeImpl (method.MethodReturnType, ObjCRuntime, StringConstants.ReleaseAttribute, out attrib); return HasAttribute (method.MethodReturnType, ObjCRuntime, StringConstants.ReleaseAttribute);
} }
protected override bool HasThisAttribute (MethodDefinition method) protected override bool HasThisAttribute (MethodDefinition method)
{ {
CustomAttribute attrib; return HasAttribute (method, "System.Runtime.CompilerServices", "ExtensionAttribute");
return TryGetAttributeImpl (method, "System.Runtime.CompilerServices", "ExtensionAttribute", out attrib);
} }
#if MTOUCH #if MTOUCH
@ -1006,6 +1052,20 @@ namespace Registrar {
return rv; return rv;
} }
protected override TypeReference [] GetLinkedAwayInterfaces (TypeReference type)
{
if (LinkContext == null)
return null;
if (LinkContext.ProtocolImplementations.TryGetValue (type.Resolve (), out var linkedAwayInterfaces) != true)
return null;
if (linkedAwayInterfaces.Count == 0)
return null;
return linkedAwayInterfaces.ToArray ();
}
protected override TypeReference GetGenericTypeDefinition (TypeReference type) protected override TypeReference GetGenericTypeDefinition (TypeReference type)
{ {
var git = type as GenericInstanceType; var git = type as GenericInstanceType;
@ -1103,7 +1163,7 @@ namespace Registrar {
} }
return null; return null;
} }
var type = tr.Resolve (); var type = ResolveType (tr);
if (type.BaseType == null) if (type.BaseType == null)
return null; return null;
@ -1157,18 +1217,16 @@ namespace Registrar {
protected override bool TryGetAttribute (TypeReference type, string attributeNamespace, string attributeType, out object attribute) protected override bool TryGetAttribute (TypeReference type, string attributeNamespace, string attributeType, out object attribute)
{ {
CustomAttribute attrib; bool res = TryGetAttribute (type.Resolve (), attributeNamespace, attributeType, out var attrib);
bool res = TryGetAttributeImpl (type.Resolve (), attributeNamespace, attributeType, out attrib);
attribute = attrib; attribute = attrib;
return res; return res;
} }
public override RegisterAttribute GetRegisterAttribute (TypeReference type) public override RegisterAttribute GetRegisterAttribute (TypeReference type)
{ {
CustomAttribute attrib;
RegisterAttribute rv = null; RegisterAttribute rv = null;
if (!TryGetAttributeImpl (type.Resolve (), Foundation, StringConstants.RegisterAttribute, out attrib)) if (!TryGetAttribute (type.Resolve (), Foundation, StringConstants.RegisterAttribute, out var attrib))
return null; return null;
if (!attrib.HasConstructorArguments) { if (!attrib.HasConstructorArguments) {
@ -1212,10 +1270,9 @@ namespace Registrar {
protected override CategoryAttribute GetCategoryAttribute (TypeReference type) protected override CategoryAttribute GetCategoryAttribute (TypeReference type)
{ {
CustomAttribute attrib;
string name = null; string name = null;
if (!TryGetAttributeImpl (type.Resolve (), ObjCRuntime, StringConstants.CategoryAttribute, out attrib)) if (!TryGetAttribute (type.Resolve (), ObjCRuntime, StringConstants.CategoryAttribute, out var attrib))
return null; return null;
if (!attrib.HasConstructorArguments) if (!attrib.HasConstructorArguments)
@ -1249,11 +1306,9 @@ namespace Registrar {
return CreateExportAttribute (GetBasePropertyInTypeHierarchy (property) ?? property); return CreateExportAttribute (GetBasePropertyInTypeHierarchy (property) ?? property);
} }
protected override ProtocolAttribute GetProtocolAttribute (TypeReference type) public override ProtocolAttribute GetProtocolAttribute (TypeReference type)
{ {
CustomAttribute attrib; if (!TryGetAttribute (type.Resolve (), Foundation, StringConstants.ProtocolAttribute, out var attrib))
if (!TryGetAttributeImpl (type.Resolve (), Foundation, StringConstants.ProtocolAttribute, out attrib))
return null; return null;
if (!attrib.HasProperties) if (!attrib.HasProperties)
@ -1266,7 +1321,7 @@ namespace Registrar {
rv.Name = (string) prop.Argument.Value; rv.Name = (string) prop.Argument.Value;
break; break;
case "WrapperType": case "WrapperType":
rv.WrapperType = (TypeDefinition) prop.Argument.Value; rv.WrapperType = ((TypeReference) prop.Argument.Value).Resolve ();
break; break;
case "IsInformal": case "IsInformal":
rv.IsInformal = (bool) prop.Argument.Value; rv.IsInformal = (bool) prop.Argument.Value;
@ -1295,13 +1350,7 @@ namespace Registrar {
{ {
var td = type.Resolve (); var td = type.Resolve ();
if (!td.HasCustomAttributes) foreach (var ca in GetCustomAttributes (td, Foundation, StringConstants.ProtocolMemberAttribute)) {
yield break;
foreach (var ca in td.CustomAttributes) {
if (!ca.Constructor.DeclaringType.Is (Foundation, StringConstants.ProtocolMemberAttribute))
continue;
var rv = new ProtocolMemberAttribute (); var rv = new ProtocolMemberAttribute ();
foreach (var prop in ca.Properties) { foreach (var prop in ca.Properties) {
switch (prop.Name) { switch (prop.Name) {
@ -1579,9 +1628,7 @@ namespace Registrar {
protected override TypeReference GetProtocolAttributeWrapperType (TypeReference type) protected override TypeReference GetProtocolAttributeWrapperType (TypeReference type)
{ {
CustomAttribute attrib; if (!TryGetAttribute (type.Resolve (), Foundation, StringConstants.ProtocolAttribute, out var attrib))
if (!TryGetAttributeImpl (type.Resolve (), Foundation, StringConstants.ProtocolAttribute, out attrib))
return null; return null;
if (attrib.HasProperties) { if (attrib.HasProperties) {
@ -1594,16 +1641,36 @@ namespace Registrar {
return null; return null;
} }
protected override IList<AdoptsAttribute> GetAdoptsAttributes (TypeReference type)
{
var attributes = GetCustomAttributes (type.Resolve (), ObjCRuntime, "AdoptsAttribute");
if (attributes == null || !attributes.Any ())
return null;
var rv = new List<AdoptsAttribute> ();
foreach (var ca in attributes) {
var attrib = new AdoptsAttribute ();
switch (ca.ConstructorArguments.Count) {
case 1:
attrib.ProtocolType = (string) ca.ConstructorArguments [0].Value;
break;
default:
throw ErrorHelper.CreateError (4124, "Invalid AdoptsAttribute found on '{0}': expected 1 constructor arguments, got {1}. Please file a bug report at https://bugzilla.xamarin.com", type.FullName, 1, ca.ConstructorArguments.Count);
}
rv.Add (attrib);
}
return rv;
}
protected override BindAsAttribute GetBindAsAttribute (PropertyDefinition property) protected override BindAsAttribute GetBindAsAttribute (PropertyDefinition property)
{ {
CustomAttribute attrib;
if (property == null) if (property == null)
return null; return null;
property = GetBasePropertyInTypeHierarchy (property); property = GetBasePropertyInTypeHierarchy (property);
if (!TryGetAttributeImpl (property, ObjCRuntime, "BindAsAttribute", out attrib)) if (!TryGetAttribute (property, ObjCRuntime, "BindAsAttribute", out var attrib))
return null; return null;
return CreateBindAsAttribute (attrib, property); return CreateBindAsAttribute (attrib, property);
@ -1611,20 +1678,18 @@ namespace Registrar {
protected override BindAsAttribute GetBindAsAttribute (MethodDefinition method, int parameter_index) protected override BindAsAttribute GetBindAsAttribute (MethodDefinition method, int parameter_index)
{ {
CustomAttribute attrib;
if (method == null) if (method == null)
return null; return null;
method = GetBaseMethodInTypeHierarchy (method); method = GetBaseMethodInTypeHierarchy (method);
if (!TryGetAttributeImpl (parameter_index == -1 ? (ICustomAttributeProvider) method.MethodReturnType : method.Parameters [parameter_index], ObjCRuntime, "BindAsAttribute", out attrib)) if (!TryGetAttribute (parameter_index == -1 ? (ICustomAttributeProvider) method.MethodReturnType : method.Parameters [parameter_index], ObjCRuntime, "BindAsAttribute", out var attrib))
return null; return null;
return CreateBindAsAttribute (attrib, method); return CreateBindAsAttribute (attrib, method);
} }
static BindAsAttribute CreateBindAsAttribute (CustomAttribute attrib, IMemberDefinition member) static BindAsAttribute CreateBindAsAttribute (ICustomAttribute attrib, IMemberDefinition member)
{ {
TypeReference originalType = null; TypeReference originalType = null;
if (attrib.HasFields) { if (attrib.HasFields) {
@ -1660,9 +1725,9 @@ namespace Registrar {
protected override ConnectAttribute GetConnectAttribute (PropertyDefinition property) protected override ConnectAttribute GetConnectAttribute (PropertyDefinition property)
{ {
CustomAttribute attrib; ICustomAttribute attrib;
if (!TryGetAttributeImpl (property, Foundation, StringConstants.ConnectAttribute, out attrib)) if (!TryGetAttribute (property, Foundation, StringConstants.ConnectAttribute, out attrib))
return null; return null;
if (!attrib.HasConstructorArguments) if (!attrib.HasConstructorArguments)
@ -1676,7 +1741,7 @@ namespace Registrar {
} }
} }
static ExportAttribute CreateExportAttribute (IMemberDefinition candidate) ExportAttribute CreateExportAttribute (IMemberDefinition candidate)
{ {
bool is_variadic = false; bool is_variadic = false;
var attribute = GetExportAttribute (candidate); var attribute = GetExportAttribute (candidate);
@ -1684,9 +1749,6 @@ namespace Registrar {
if (attribute == null) if (attribute == null)
return null; return null;
if (attribute.Constructor.Parameters.Count != attribute.ConstructorArguments.Count)
throw ErrorHelper.CreateError (4124, "Invalid ExportAttribute found on '{0}.{1}'. Please file a bug report at http://bugzilla.xamarin.com", candidate.DeclaringType.FullName, candidate.Name);
if (attribute.HasProperties) { if (attribute.HasProperties) {
foreach (var prop in attribute.Properties) { foreach (var prop in attribute.Properties) {
if (prop.Name == "IsVariadic") { if (prop.Name == "IsVariadic") {
@ -1712,11 +1774,11 @@ namespace Registrar {
} }
// [Export] is not sealed anymore - so we cannot simply compare strings // [Export] is not sealed anymore - so we cannot simply compare strings
static CustomAttribute GetExportAttribute (ICustomAttributeProvider candidate) ICustomAttribute GetExportAttribute (ICustomAttributeProvider candidate)
{ {
if (!candidate.HasCustomAttributes) if (!candidate.HasCustomAttributes)
return null; return null;
foreach (CustomAttribute ca in candidate.CustomAttributes) { foreach (CustomAttribute ca in candidate.CustomAttributes) {
if (ca.Constructor.DeclaringType.Inherits (Foundation, StringConstants.ExportAttribute)) if (ca.Constructor.DeclaringType.Inherits (Foundation, StringConstants.ExportAttribute))
return ca; return ca;
@ -1841,7 +1903,18 @@ namespace Registrar {
if (type.IsNested) if (type.IsNested)
return false; return false;
var aname = type.Module.Assembly.Name.Name; string aname;
if (type.Module == null) {
// This type was probably linked away
if (LinkContext.GetLinkedAwayType (type, out var module) != null) {
aname = module.Assembly.Name.Name;
} else {
aname = string.Empty;
}
} else {
aname = type.Module.Assembly.Name.Name;
}
if (aname != PlatformAssembly) if (aname != PlatformAssembly)
return false; return false;
@ -1852,6 +1925,11 @@ namespace Registrar {
} }
} }
static bool IsLinkedAway (TypeReference tr)
{
return tr.Module == null;
}
void CheckNamespace (ObjCType objctype, List<Exception> exceptions) void CheckNamespace (ObjCType objctype, List<Exception> exceptions)
{ {
CheckNamespace (objctype.Type, exceptions); CheckNamespace (objctype.Type, exceptions);
@ -2151,7 +2229,7 @@ namespace Registrar {
string ToObjCParameterType (TypeReference type, string descriptiveMethodName, List<Exception> exceptions, MemberReference inMethod) string ToObjCParameterType (TypeReference type, string descriptiveMethodName, List<Exception> exceptions, MemberReference inMethod)
{ {
TypeDefinition td = type.Resolve (); TypeDefinition td = ResolveType (type);
var reftype = type as ByReferenceType; var reftype = type as ByReferenceType;
ArrayType arrtype = type as ArrayType; ArrayType arrtype = type as ArrayType;
GenericParameter gp = type as GenericParameter; GenericParameter gp = type as GenericParameter;
@ -2453,6 +2531,12 @@ namespace Registrar {
} }
} }
class ProtocolInfo
{
public uint TokenReference;
public ObjCType Protocol;
}
class SkippedType class SkippedType
{ {
public TypeReference Skipped; public TypeReference Skipped;
@ -2492,9 +2576,20 @@ namespace Registrar {
var map_init = new AutoIndentStringBuilder (); var map_init = new AutoIndentStringBuilder ();
var map_dict = new Dictionary<ObjCType, int> (); // maps ObjCType to its index in the map var map_dict = new Dictionary<ObjCType, int> (); // maps ObjCType to its index in the map
var map_entries = 0; var map_entries = 0;
var protocol_wrapper_map = new Dictionary<uint, Tuple<ObjCType, uint>> ();
var protocols = new List<ProtocolInfo> ();
var i = 0; var i = 0;
bool needs_protocol_map = false;
// Check if we need the protocol map.
// We don't need it if the linker removed the method ObjCRuntime.Runtime.GetProtocolForType,
// or if we're not registering protocols.
if (App.Optimizations.RegisterProtocols == true) {
var asm = input_assemblies.FirstOrDefault ((v) => v.Name.Name == PlatformAssembly);
needs_protocol_map = asm?.MainModule.GetType (!IsDualBuild ? (CompatNamespace + ".ObjCRuntime") : "ObjCRuntime", "Runtime")?.Methods.Any ((v) => v.Name == "GetProtocolForType") == true;
}
map.AppendLine ("static MTClassMap __xamarin_class_map [] = {"); map.AppendLine ("static MTClassMap __xamarin_class_map [] = {");
if (string.IsNullOrEmpty (single_assembly)) { if (string.IsNullOrEmpty (single_assembly)) {
map_init.AppendLine ("void xamarin_create_classes () {"); map_init.AppendLine ("void xamarin_create_classes () {");
@ -2564,11 +2659,13 @@ namespace Registrar {
skip.Clear (); skip.Clear ();
uint token_ref = uint.MaxValue;
if (!@class.IsProtocol && !@class.IsCategory) { if (!@class.IsProtocol && !@class.IsCategory) {
if (!isPlatformType) if (!isPlatformType)
customTypeCount++; customTypeCount++;
CheckNamespace (@class, exceptions); CheckNamespace (@class, exceptions);
token_ref = CreateTokenReference (@class.Type, TokenType.TypeDef);
map.AppendLine ("{{ NULL, 0x{1:X} /* #{3} '{0}' => '{2}' */ }},", map.AppendLine ("{{ NULL, 0x{1:X} /* #{3} '{0}' => '{2}' */ }},",
@class.ExportedName, @class.ExportedName,
CreateTokenReference (@class.Type, TokenType.TypeDef), CreateTokenReference (@class.Type, TokenType.TypeDef),
@ -2607,6 +2704,16 @@ namespace Registrar {
map_init.AppendLine ("__xamarin_class_map [{1}].handle = {0};", get_class, i++); map_init.AppendLine ("__xamarin_class_map [{1}].handle = {0};", get_class, i++);
} }
if (@class.IsProtocol && @class.ProtocolWrapperType != null) {
if (token_ref == uint.MaxValue)
token_ref = CreateTokenReference (@class.Type, TokenType.TypeDef);
protocol_wrapper_map.Add (token_ref, new Tuple<ObjCType, uint> (@class, CreateTokenReference (@class.ProtocolWrapperType, TokenType.TypeDef)));
if (needs_protocol_map) {
protocols.Add (new ProtocolInfo { TokenReference = token_ref, Protocol = @class });
CheckNamespace (@class, exceptions);
}
}
if (@class.IsWrapper && isPlatformType) if (@class.IsWrapper && isPlatformType)
continue; continue;
@ -2653,7 +2760,18 @@ namespace Registrar {
iface.Append (any_protocols ? ", " : "<"); iface.Append (any_protocols ? ", " : "<");
any_protocols = true; any_protocols = true;
iface.Append (tp.Protocols [p].ProtocolName); iface.Append (tp.Protocols [p].ProtocolName);
CheckNamespace (tp.Protocols [p], exceptions); var proto = tp.Protocols [p].Type;
if (!IsLinkedAway (proto))
CheckNamespace (proto, exceptions);
}
}
if (App.Optimizations.RegisterProtocols == true && tp.AdoptedProtocols != null) {
for (int p = 0; p < tp.AdoptedProtocols.Length; p++) {
if (tp.AdoptedProtocols [p] == "UIAppearance")
continue; // This is not a real protocol
iface.Append (any_protocols ? ", " : "<");
any_protocols = true;
iface.Append (tp.AdoptedProtocols [p]);
} }
} }
tp = tp.BaseType; tp = tp.BaseType;
@ -2853,16 +2971,57 @@ namespace Registrar {
map.AppendLine (); map.AppendLine ();
} }
if (protocol_wrapper_map.Count > 0) {
var ordered = protocol_wrapper_map.OrderBy ((v) => v.Key);
map.AppendLine ("static const MTProtocolWrapperMap __xamarin_protocol_wrapper_map [] = {");
foreach (var p in ordered) {
map.AppendLine ("{{ 0x{0:X} /* {1} */, 0x{2:X} /* {3} */ }},", p.Key, p.Value.Item1.Name, p.Value.Item2, p.Value.Item1.ProtocolWrapperType.Name);
}
map.AppendLine ("};");
map.AppendLine ();
}
if (needs_protocol_map && protocols.Count > 0) {
var ordered = protocols.OrderBy ((v) => v.TokenReference);
map.AppendLine ("static const uint32_t __xamarin_protocol_tokens [] = {");
foreach (var p in ordered)
map.AppendLine ("0x{0:X}, /* {1} */", p.TokenReference, p.Protocol.Type.FullName);
map.AppendLine ("};");
map.AppendLine ("static const Protocol* __xamarin_protocols [] = {");
foreach (var p in ordered) {
bool use_dynamic = false;
#if MTOUCH
switch (p.Protocol.ProtocolName) {
case "CAMetalDrawable": // The header isn't available for the simulator.
use_dynamic = IsSimulator;
break;
}
#endif
if (use_dynamic) {
map.AppendLine ("objc_getProtocol (\"{0}\"), /* {1} */", p.Protocol.ProtocolName, p.Protocol.Type.FullName);
} else {
map.AppendLine ("@protocol ({0}), /* {1} */", p.Protocol.ProtocolName, p.Protocol.Type.FullName);
}
}
map.AppendLine ("};");
}
map.AppendLine ("static struct MTRegistrationMap __xamarin_registration_map = {"); map.AppendLine ("static struct MTRegistrationMap __xamarin_registration_map = {");
map.AppendLine ("__xamarin_registration_assemblies,"); map.AppendLine ("__xamarin_registration_assemblies,");
map.AppendLine ("__xamarin_class_map,"); map.AppendLine ("__xamarin_class_map,");
map.AppendLine (full_token_reference_count == 0 ? "NULL," : "__xamarin_token_references,"); map.AppendLine (full_token_reference_count == 0 ? "NULL," : "__xamarin_token_references,");
map.AppendLine (skipped_types.Count == 0 ? "NULL," : "__xamarin_skipped_map,"); map.AppendLine (skipped_types.Count == 0 ? "NULL," : "__xamarin_skipped_map,");
map.AppendLine (protocol_wrapper_map.Count == 0 ? "NULL," : "__xamarin_protocol_wrapper_map,");
if (needs_protocol_map && protocols.Count > 0) {
map.AppendLine ("{ __xamarin_protocol_tokens, __xamarin_protocols },");
} else {
map.AppendLine ("{ NULL, NULL },");
}
map.AppendLine ("{0},", count); map.AppendLine ("{0},", count);
map.AppendLine ("{0},", i); map.AppendLine ("{0},", i);
map.AppendLine ("{0},", customTypeCount); map.AppendLine ("{0},", customTypeCount);
map.AppendLine ("{0},", full_token_reference_count); map.AppendLine ("{0},", full_token_reference_count);
map.AppendLine ("{0}", skipped_types.Count); map.AppendLine ("{0},", skipped_types.Count);
map.AppendLine ("{0},", protocol_wrapper_map.Count);
map.AppendLine ("{0}", needs_protocol_map ? protocols.Count : 0);
map.AppendLine ("};"); map.AppendLine ("};");
@ -4501,4 +4660,9 @@ namespace Registrar {
public bool IsWrapper { get; set; } public bool IsWrapper { get; set; }
public bool SkipRegistration { get; set; } public bool SkipRegistration { get; set; }
} }
class AdoptsAttribute : Attribute
{
public string ProtocolType { get; set; }
}
} }

Просмотреть файл

@ -9,6 +9,8 @@ using Mono.Cecil.Cil;
using Mono.Linker; using Mono.Linker;
using Mono.Tuner; using Mono.Tuner;
using Registrar;
namespace Xamarin.Linker.Steps { namespace Xamarin.Linker.Steps {
// Generated backend fields inside <product>.dll are also removed if only used (i.e. set to null) // Generated backend fields inside <product>.dll are also removed if only used (i.e. set to null)
@ -19,6 +21,10 @@ namespace Xamarin.Linker.Steps {
protected string ProductAssembly { get; private set; } protected string ProductAssembly { get; private set; }
bool RegisterProtocols {
get { return LinkContext.App.Optimizations.RegisterProtocols == true; }
}
public override void Process (LinkContext context) public override void Process (LinkContext context)
{ {
ProductAssembly = (Profile.Current as BaseProfile).ProductAssembly; ProductAssembly = (Profile.Current as BaseProfile).ProductAssembly;
@ -148,7 +154,17 @@ namespace Xamarin.Linker.Steps {
protected override TypeDefinition MarkType (TypeReference reference) protected override TypeDefinition MarkType (TypeReference reference)
{ {
try { try {
return base.MarkType (reference); var td = base.MarkType (reference);
// We're removing the Protocol attribute, which points to its wrapper type.
// But we need the wrapper type if the protocol interface is marked, so manually mark it.
if (td != null && td.IsInterface) {
var proto = LinkContext.StaticRegistrar.GetProtocolAttribute (td);
if (proto?.WrapperType != null)
MarkType (proto.WrapperType);
}
return td;
} catch (Exception e) { } catch (Exception e) {
// we need a way to know where (not just what) went wrong (e.g. debugging symbols being incorrect) // we need a way to know where (not just what) went wrong (e.g. debugging symbols being incorrect)
e.Data ["TypeReference"] = reference.ToString (); e.Data ["TypeReference"] = reference.ToString ();
@ -212,9 +228,49 @@ namespace Xamarin.Linker.Steps {
var method = base.MarkMethod (reference); var method = base.MarkMethod (reference);
if (method == null) if (method == null)
return null; return null;
var t = method.DeclaringType;
// We have special processing that prevents protocol interfaces from being marked if they're
// only used by being implemented by a class, but the linker will not mark interfaces if a method implemented by an interface
// is marked: this means we need special processing to preserve a protocol interface whose methods have been implemented.
if (RegisterProtocols && t.HasInterfaces && method.IsVirtual) {
foreach (var r in t.Interfaces) {
var i = r.InterfaceType.Resolve ();
if (i == null)
continue;
if (Annotations.IsMarked (i))
continue;
if (!LinkContext.StaticRegistrar.HasAttribute (i, Namespaces.Foundation, "ProtocolAttribute"))
continue;
var isProtocolImplementation = false;
// Are there any explicit overrides?
foreach (var @override in method.Overrides) {
if (!i.Methods.Contains (@override.Resolve ()))
continue;
isProtocolImplementation = true;
break;
}
if (!isProtocolImplementation) {
// Are there any implicit overrides (identical name and signature)?
foreach (var imethod in i.Methods) {
if (!StaticRegistrar.MethodMatch (imethod, method))
continue;
isProtocolImplementation = true;
break;
}
}
if (isProtocolImplementation) {
MarkType (r.InterfaceType);
Bundler.Driver.Log (9, "Marking {0} because the method {1} implements one of its methods.", r.InterfaceType, method.FullName);
}
}
}
// special processing to find [BlockProxy] attributes in _Extensions types // special processing to find [BlockProxy] attributes in _Extensions types
// ref: https://bugzilla.xamarin.com/show_bug.cgi?id=23540 // ref: https://bugzilla.xamarin.com/show_bug.cgi?id=23540
var t = method.DeclaringType;
if (method.HasCustomAttributes && t.HasInterfaces) { if (method.HasCustomAttributes && t.HasInterfaces) {
string selector = null; string selector = null;
foreach (var r in t.Interfaces) { foreach (var r in t.Interfaces) {
@ -278,5 +334,37 @@ namespace Xamarin.Linker.Steps {
} }
return method; return method;
} }
protected override void MarkInterfaceImplementation (TypeDefinition type, InterfaceImplementation iface)
{
if (RegisterProtocols) {
// If we're registering protocols, we can remove interfaces that represent protocols.
// The linker will automatically mark interfaces a class implements, but we have to
// override the linker behavior for interfaces that represent protocols for those
// interfaces to be removed.
var mark = false;
var interfaceType = iface.InterfaceType.Resolve ();
var isProtocol = type.IsNSObject (LinkContext) && interfaceType.HasCustomAttribute (LinkContext, Namespaces.Foundation, "ProtocolAttribute");
if (IgnoreScope (type.Scope)) {
// We're not linking the current assembly, which means the interface should be marked.
mark = true;
} else if (!isProtocol) {
// We only skip interfaces that represent protocols.
mark = true;
}
if (!mark) {
if (isProtocol)
LinkContext.StoreProtocolMethods (interfaceType);
return;
}
}
base.MarkInterfaceImplementation (type, iface);
}
} }
} }

Просмотреть файл

@ -45,6 +45,11 @@ namespace Xamarin.Linker {
case "RequiresSuperAttribute": case "RequiresSuperAttribute":
case "BindingImplAttribute": case "BindingImplAttribute":
return attr_type.Namespace == Namespaces.ObjCRuntime; return attr_type.Namespace == Namespaces.ObjCRuntime;
case "AdoptsAttribute":
return attr_type.Namespace == Namespaces.ObjCRuntime && LinkContext.App.Optimizations.RegisterProtocols == true;
case "ProtocolAttribute":
case "ProtocolMemberAttribute":
return attr_type.Namespace == Namespaces.Foundation && LinkContext.App.Optimizations.RegisterProtocols == true;
default: default:
return base.IsRemovedAttribute (attribute); return base.IsRemovedAttribute (attribute);
} }
@ -61,10 +66,18 @@ namespace Xamarin.Linker {
case "IntroducedAttribute": case "IntroducedAttribute":
LinkContext.StoreCustomAttribute (provider, attribute, "Availability"); LinkContext.StoreCustomAttribute (provider, attribute, "Availability");
break; break;
case "AdoptsAttribute":
case "BindingImplAttribute": case "BindingImplAttribute":
LinkContext.StoreCustomAttribute (provider, attribute); LinkContext.StoreCustomAttribute (provider, attribute);
break; break;
} }
} else if (attr_type.Namespace == Namespaces.Foundation) {
switch (attr_type.Name) {
case "ProtocolAttribute":
case "ProtocolMemberAttribute":
LinkContext.StoreCustomAttribute (provider, attribute);
break;
}
} }
base.WillRemoveAttribute (provider, attribute); base.WillRemoveAttribute (provider, attribute);

Просмотреть файл

@ -0,0 +1,45 @@
// Copyright 2018 Microsoft Inc. All rights reserved.
using System;
using System.Collections.Generic;
using Mono.Cecil;
using Mono.Linker;
using Mono.Linker.Steps;
using Xamarin.Tuner;
namespace Xamarin.Linker {
// CoreSweepStep is shared between Xamarin.Mac and Xamarin.iOS
public class CoreSweepStep : MobileSweepStep {
public CoreSweepStep (bool sweepSymbols)
: base (sweepSymbols)
{
}
protected DerivedLinkContext LinkContext {
get {
return (DerivedLinkContext) base.Context;
}
}
protected override void InterfaceRemoved (TypeDefinition type, InterfaceImplementation iface)
{
base.InterfaceRemoved (type, iface);
// The static registrar needs access to the interfaces for protocols, so keep them around.
if (!LinkContext.ProtocolImplementations.TryGetValue (type, out var list))
LinkContext.ProtocolImplementations [type] = list = new List<TypeDefinition> ();
list.Add (iface.InterfaceType.Resolve ());
}
protected override void ElementRemoved (IMetadataTokenProvider element)
{
base.ElementRemoved (element);
if (element is TypeDefinition td)
LinkContext.AddLinkedAwayType (td);
}
}
}

Просмотреть файл

@ -1,11 +1,13 @@
// Copyright 2012-2013, 2015 Xamarin Inc. All rights reserved. // Copyright 2012-2013, 2015 Xamarin Inc. All rights reserved.
using System; using System;
using System.Collections; using System.Collections.Generic;
using Mono.Cecil; using Mono.Cecil;
using Mono.Linker; using Mono.Linker;
using Mono.Linker.Steps; using Mono.Linker.Steps;
using Xamarin.Tuner;
namespace Xamarin.Linker { namespace Xamarin.Linker {
// MobileMarkStep process a bit more data and that can be used to sweep // MobileMarkStep process a bit more data and that can be used to sweep
@ -55,4 +57,4 @@ namespace Xamarin.Linker {
SweepCollectionNonAttributable (main.ModuleReferences); SweepCollectionNonAttributable (main.ModuleReferences);
} }
} }
} }

Просмотреть файл

@ -8,7 +8,7 @@ using Xamarin.Linker;
namespace MonoTouch.Tuner { namespace MonoTouch.Tuner {
public class MonoTouchSweepStep : MobileSweepStep { public class MonoTouchSweepStep : CoreSweepStep {
public MonoTouchSweepStep (LinkerOptions options) public MonoTouchSweepStep (LinkerOptions options)
: base (options.LinkSymbols) : base (options.LinkSymbols)

Просмотреть файл

@ -85,6 +85,7 @@ tuner_sources = \
$(TOP)/tools/linker/CoreRemoveAttributes.cs \ $(TOP)/tools/linker/CoreRemoveAttributes.cs \
$(TOP)/tools/linker/CoreHttpMessageHandler.cs \ $(TOP)/tools/linker/CoreHttpMessageHandler.cs \
$(TOP)/tools/linker/CoreRemoveSecurity.cs \ $(TOP)/tools/linker/CoreRemoveSecurity.cs \
$(TOP)/tools/linker/CoreSweepStep.cs \
$(TOP)/tools/linker/ObjCExtensions.cs \ $(TOP)/tools/linker/ObjCExtensions.cs \
$(TOP)/tools/linker/MarkNSObjects.cs \ $(TOP)/tools/linker/MarkNSObjects.cs \
$(TOP)/tools/linker/MobileExtensions.cs \ $(TOP)/tools/linker/MobileExtensions.cs \

Просмотреть файл

@ -83,6 +83,7 @@ namespace MonoMac.Tuner {
context = CreateLinkContext (options, pipeline); context = CreateLinkContext (options, pipeline);
context.Resolver.AddSearchDirectory (options.OutputDirectory); context.Resolver.AddSearchDirectory (options.OutputDirectory);
context.KeepTypeForwarderOnlyAssemblies = (Profile.Current is XamarinMacProfile); context.KeepTypeForwarderOnlyAssemblies = (Profile.Current is XamarinMacProfile);
options.Target.LinkContext = (context as MonoMacLinkContext);
try { try {
pipeline.Process (context); pipeline.Process (context);
@ -178,7 +179,7 @@ namespace MonoMac.Tuner {
pipeline.AppendStep (new MonoMacMarkStep ()); pipeline.AppendStep (new MonoMacMarkStep ());
pipeline.AppendStep (new MacRemoveResources (options)); pipeline.AppendStep (new MacRemoveResources (options));
pipeline.AppendStep (new MobileSweepStep (options.LinkSymbols)); pipeline.AppendStep (new CoreSweepStep (options.LinkSymbols));
pipeline.AppendStep (new CleanStep ()); pipeline.AppendStep (new CleanStep ());
pipeline.AppendStep (new MonoMacNamespaces ()); pipeline.AppendStep (new MonoMacNamespaces ());

Просмотреть файл

@ -1476,7 +1476,6 @@ namespace Xamarin.Bundler {
Mono.Linker.LinkContext context; Mono.Linker.LinkContext context;
MonoMac.Tuner.Linker.Process (options, out context, out resolved_assemblies); MonoMac.Tuner.Linker.Process (options, out context, out resolved_assemblies);
BuildTarget.LinkContext = (context as MonoMacLinkContext);
// Idealy, this would be handled by Linker.Process above. However in the non-linking case // Idealy, this would be handled by Linker.Process above. However in the non-linking case
// we do not run MobileMarkStep which generates the pinvoke list. Hack around this for now // we do not run MobileMarkStep which generates the pinvoke list. Hack around this for now

Просмотреть файл

@ -251,6 +251,9 @@
<Compile Include="..\linker\CorePreserveCode.cs"> <Compile Include="..\linker\CorePreserveCode.cs">
<Link>Xamarin.Linker\CorePreserveCode.cs</Link> <Link>Xamarin.Linker\CorePreserveCode.cs</Link>
</Compile> </Compile>
<Compile Include="..\linker\CoreSweepStep.cs">
<Link>Xamarin.Linker\CoreSweepStep.cs</Link>
</Compile>
<Compile Include="..\linker\MobileProfile.cs"> <Compile Include="..\linker\MobileProfile.cs">
<Link>Xamarin.Linker\MobileProfile.cs</Link> <Link>Xamarin.Linker\MobileProfile.cs</Link>
</Compile> </Compile>

Просмотреть файл

@ -84,6 +84,7 @@ LINKER_SOURCES = \
$(TOP)/tools/linker/CorePreserveCode.cs \ $(TOP)/tools/linker/CorePreserveCode.cs \
$(TOP)/tools/linker/CoreRemoveAttributes.cs \ $(TOP)/tools/linker/CoreRemoveAttributes.cs \
$(TOP)/tools/linker/CoreRemoveSecurity.cs \ $(TOP)/tools/linker/CoreRemoveSecurity.cs \
$(TOP)/tools/linker/CoreSweepStep.cs \
$(TOP)/tools/linker/ExceptionalSubStep.cs \ $(TOP)/tools/linker/ExceptionalSubStep.cs \
$(TOP)/tools/linker/MarkNSObjects.cs \ $(TOP)/tools/linker/MarkNSObjects.cs \
$(TOP)/tools/linker/MobileExtensions.cs \ $(TOP)/tools/linker/MobileExtensions.cs \

Просмотреть файл

@ -260,6 +260,9 @@
<Compile Include="..\linker\MobileResolveMainAssemblyStep.cs"> <Compile Include="..\linker\MobileResolveMainAssemblyStep.cs">
<Link>Xamarin.Linker\MobileResolveMainAssemblyStep.cs</Link> <Link>Xamarin.Linker\MobileResolveMainAssemblyStep.cs</Link>
</Compile> </Compile>
<Compile Include="..\linker\CoreSweepStep.cs">
<Link>Xamarin.Linker\CoreSweepStep.cs</Link>
</Compile>
<Compile Include="..\common\Driver.cs"> <Compile Include="..\common\Driver.cs">
<Link>external\Driver.cs</Link> <Link>external\Driver.cs</Link>
</Compile> </Compile>