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:
Коммит
60a8731b35
|
@ -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>
|
||||||
|
|
Загрузка…
Ссылка в новой задаче