[registrar] Report a warning when the registrar export an abstract INativeObject type to Objective-C. (#6659)

* [registrar] Report a warning when the registrar export an abstract INativeObject type to Objective-C.

Exporting abstract types to Objective-C can lead to problems when at runtime
we're asked to create an instance of such a type (which we can't), so warn
when this happens.

This would have caught #6655, and the problems explained in #4969 as well.

Since this may trigger for code that's currently working fine, I'm making it a
warning instead of an error (which means adding some extra code to be able to
easily report warnings from the generator code).

* Don't assume a TypeReference can be successfully resolved every time.
This commit is contained in:
Rolf Bjarne Kvinge 2019-09-10 04:35:59 -07:00 коммит произвёл GitHub
Родитель 14559e35bd
Коммит 15f0af7a1a
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
8 изменённых файлов: 177 добавлений и 30 удалений

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

@ -583,6 +583,22 @@ complicated to get it right when doing it manually.
If this is not the case, please file a [bug report](https://github.com/xamarin/xamarin-macios/issues/new) with a test case.
### MM4178: The registrar found the abstract type '{type}' in the signature for '{member}'. Abstract types should not be used in the signature for a member exported to Objective-C.
This is a warning, indicating a potential problem where a method or property
has a parameter or return type which is abstract. The potential problem occurs
at runtime, when the Xamarin.Mac runtime may need to create an instance of
such a type, which will fail if the type is abstract.
Possible solutions:
* Modify the signature in question to not use an abstract type.
* Make the type not abstract.
If this is an API exposed by Xamarin, please file a new issue on
[github](https://github.com/xamarin/xamarin-macios/issues/new), if it's a
third-party binding, please contact the vendor.
## MM5xxx: GCC and toolchain
### MM51xx: compilation

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

@ -2545,6 +2545,22 @@ If this is not the case, please file a [bug report](https://github.com/xamarin/x
The name of an Objective-C protocol can't contain certain characters which means that the `Adopts` attribute on the corresponding managed class can't have the `ProtocolType` parameter containing them. Please refer to the provided error message and fix accordingly.
### MT4178: The registrar found the abstract type '{type}' in the signature for '{member}'. Abstract types should not be used in the signature for a member exported to Objective-C.
This is a warning, indicating a potential problem where a method or property
has a parameter or return type which is abstract. The potential problem occurs
at runtime, when the Xamarin.iOS runtime may need to create an instance of
such a type, which will fail if the type is abstract.
Possible solutions:
* Modify the signature in question to not use an abstract type.
* Make the type not abstract.
If this is an API exposed by Xamarin, please file a new issue on
[github](https://github.com/xamarin/xamarin-macios/issues/new), if it's a
third-party binding, please contact the vendor.
# MT5xxx: GCC and toolchain error messages
### MT51xx: Compilation

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

@ -524,18 +524,22 @@ namespace Registrar {
return false;
}
protected override Exception CreateException (int code, Exception innerException, MethodBase method, string message, params object[] args)
protected override Exception CreateExceptionImpl (int code, bool error, Exception innerException, MethodBase method, string message, params object[] args)
{
// There doesn't seem to be a way to find the source code location
// for the method using System.Reflection.
return ErrorHelper.CreateError (code, innerException, message, args);
if (error)
return ErrorHelper.CreateError (code, innerException, message, args);
return ErrorHelper.CreateWarning (code, innerException, message, args);
}
protected override Exception CreateException (int code, Exception innerException, Type type, string message, params object [] args)
protected override Exception CreateExceptionImpl (int code, bool error, Exception innerException, Type type, string message, params object [] args)
{
// There doesn't seem to be a way to find the source code location
// for the method using System.Reflection.
return ErrorHelper.CreateError (code, innerException, message, args);
if (error)
return ErrorHelper.CreateError (code, innerException, message, args);
return ErrorHelper.CreateWarning (code, innerException, message, args);
}
protected override string GetAssemblyQualifiedName (Type type)
@ -635,6 +639,11 @@ namespace Registrar {
return type.IsInterface;
}
protected override bool IsAbstract (Type type)
{
return type.IsAbstract;
}
protected override bool IsINativeObject (Type type)
{
return typeof (INativeObject).IsAssignableFrom (type);

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

@ -278,6 +278,11 @@ namespace ObjCRuntime {
return new ProductException (code, false, message, args);
}
public static ProductException CreateWarning (int code, Exception innerException, string message, params object [] args)
{
return new ProductException (code, false, innerException, message, args);
}
public static void Error (int code, Exception innerException, string message, params object[] args)
{
throw new ProductException (code, true, innerException, message, args);

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

@ -1110,6 +1110,7 @@ namespace Registrar {
protected abstract bool IsGenericType (TType type);
protected abstract bool IsGenericMethod (TMethod method);
protected abstract bool IsInterface (TType type);
protected abstract bool IsAbstract (TType type);
protected abstract TType GetGenericTypeDefinition (TType type);
protected abstract bool VerifyIsConstrainedToNSObject (TType type, out TType constrained_type);
protected abstract TType GetEnumUnderlyingType (TType type);
@ -1120,8 +1121,8 @@ namespace Registrar {
protected abstract bool IsSimulatorOrDesktop { get; }
protected abstract bool Is64Bits { get; }
protected abstract bool IsDualBuildImpl { get; }
protected abstract Exception CreateException (int code, Exception innerException, TMethod method, string message, params object[] args);
protected abstract Exception CreateException (int code, Exception innerException, TType type, string message, params object [] args);
protected abstract Exception CreateExceptionImpl (int code, bool error, Exception innerException, TMethod method, string message, params object[] args);
protected abstract Exception CreateExceptionImpl (int code, bool error, Exception innerException, TType type, string message, params object [] args);
protected abstract string PlatformName { get; }
public abstract TType FindType (TType relative, string @namespace, string name);
protected abstract IEnumerable<TMethod> FindMethods (TType type, string name); // will return null if nothing was found
@ -1343,43 +1344,78 @@ namespace Registrar {
protected Exception CreateException (int code, string message, params object[] args)
{
return CreateException (code, (TMethod)null, message, args);
return CreateExceptionImpl (code, true, message, args);
}
protected Exception CreateException (int code, TMethod method, string message, params object[] args)
{
return CreateException (code, null, method, message, args);
return CreateExceptionImpl (code, true, method, message, args);
}
protected Exception CreateException (int code, TProperty property, string message, params object[] args)
{
return CreateException (code, null, property, message, args);
return CreateExceptionImpl (code, true, property, message, args);
}
protected Exception CreateException (int code, TType type, string message, params object [] args)
{
return CreateException (code, null, type, message, args);
return CreateExceptionImpl (code, true, type, message, args);
}
protected Exception CreateException (int code, Exception innerException, TProperty property, string message, params object[] args)
{
if (property == null)
return CreateException (code, innerException, (TMethod) null, message, args);
var getter = GetGetMethod (property);
if (getter != null)
return CreateException (code, innerException, getter, message, args);
return CreateException (code, innerException, GetSetMethod (property), message, args);
return CreateExceptionImpl (code, true, innerException, property, message, args);
}
Exception CreateException (int code, ObjCMember member, string message, params object[] args)
Exception CreateExceptionImpl (int code, bool error, string message, params object [] args)
{
return CreateExceptionImpl (code, error, (TMethod) null, message, args);
}
Exception CreateExceptionImpl (int code, bool error, TMethod method, string message, params object [] args)
{
return CreateExceptionImpl (code, error, null, method, message, args);
}
Exception CreateExceptionImpl (int code, bool error, TProperty property, string message, params object [] args)
{
return CreateExceptionImpl (code, error, null, property, message, args);
}
Exception CreateExceptionImpl (int code, bool error, TType type, string message, params object [] args)
{
return CreateExceptionImpl (code, error, null, type, message, args);
}
Exception CreateExceptionImpl (int code, bool error, Exception innerException, TProperty property, string message, params object [] args)
{
if (property == null)
return CreateExceptionImpl (code, error, innerException, (TMethod) null, message, args);
var getter = GetGetMethod (property);
if (getter != null)
return CreateExceptionImpl (code, error, innerException, getter, message, args);
return CreateExceptionImpl (code, error, innerException, GetSetMethod (property), message, args);
}
Exception CreateException (int code, ObjCMember member, string message, params object [] args)
{
return CreateExceptionImpl (code, true, member, message, args);
}
Exception CreateExceptionImpl (int code, bool error, ObjCMember member, string message, params object[] args)
{
var method = member as ObjCMethod;
if (method != null)
return CreateException (code, method.Method, message, args);
return CreateExceptionImpl (code, error, method.Method, message, args);
var property = member as ObjCProperty;
if (property != null)
return CreateException (code, property.Property, message, args);
return CreateException (code, message, args);
return CreateExceptionImpl (code, error, property.Property, message, args);
return CreateExceptionImpl (code, error, message, args);
}
Exception CreateWarning (int code, ObjCMember member, string message, params object [] args)
{
return CreateExceptionImpl (code, false, member, message, args);
}
protected string GetDescriptiveMethodName (TMethod method)
@ -2614,6 +2650,8 @@ namespace Registrar {
return "#";
if (IsINativeObject (type)) {
if (!IsGenericType (type) && !IsInterface (type) && !IsNSObject (type) && IsAbstract (type))
ErrorHelper.Show (CreateWarning (4178, member, $"The registrar found the abstract type '{type.FullName}' in the signature for '{member.FullName}'. Abstract types should not be used in the signature for a member exported to Objective-C."));
if (IsNSObject (type) && forProperty) {
return "@\"" + GetExportedTypeName (type) + "\"";
} else {

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

@ -1794,6 +1794,55 @@ class CTP4 : CTP3 {
}
}
}
[Test]
public void MT4178 ()
{
using (var mtouch = new MTouchTool ()) {
var code = @"
using System;
using Foundation;
using ObjCRuntime;
abstract class SomeNativeObject : INativeObject
{
public IntPtr Handle { get; set; }
SomeNativeObject (IntPtr handle, bool owns) { }
}
class C : NSObject {
[Export (""M1:"")]
public void M1 (SomeNativeObject obj)
{
}
[Export (""M2"")]
public SomeNativeObject M2 ()
{
return null;
}
[Export (""M3"")]
public SomeNativeObject M3 {
get;
set;
}
static void Main () {}
}
";
mtouch.CreateTemporaryApp (code: code, extraArg: "-debug");
mtouch.Registrar = MTouchRegistrar.Static;
mtouch.Linker = MTouchLinker.DontLink;
mtouch.AssertExecute (MTouchAction.BuildSim, "build");
mtouch.AssertWarningCount (4);
mtouch.AssertWarning (4178, $"The registrar found the abstract type 'SomeNativeObject' in the signature for 'C.get_M3'. Abstract types should not be used in the signature for a member exported to Objective-C.", "testApp.cs", 27);
mtouch.AssertWarning (4178, $"The registrar found the abstract type 'SomeNativeObject' in the signature for 'C.set_M3'. Abstract types should not be used in the signature for a member exported to Objective-C.", "testApp.cs", 28);
mtouch.AssertWarning (4178, $"The registrar found the abstract type 'SomeNativeObject' in the signature for 'C.M1'. Abstract types should not be used in the signature for a member exported to Objective-C.", "testApp.cs", 16);
mtouch.AssertWarning (4178, $"The registrar found the abstract type 'SomeNativeObject' in the signature for 'C.M2'. Abstract types should not be used in the signature for a member exported to Objective-C.", "testApp.cs", 21);
}
}
}
}

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

@ -616,14 +616,14 @@ namespace Registrar {
}
}
protected override Exception CreateException (int code, Exception innerException, MethodDefinition method, string message, params object[] args)
protected override Exception CreateExceptionImpl (int code, bool error, Exception innerException, MethodDefinition method, string message, params object[] args)
{
return ErrorHelper.CreateError (App, code, innerException, method, message, args);
return ErrorHelper.Create (App, code, error, innerException, method, message, args);
}
protected override Exception CreateException (int code, Exception innerException, TypeReference type, string message, params object [] args)
protected override Exception CreateExceptionImpl (int code, bool error, Exception innerException, TypeReference type, string message, params object [] args)
{
return ErrorHelper.CreateError (App, code, innerException, type, message, args);
return ErrorHelper.Create (App, code, error, innerException, type, message, args);
}
protected override bool ContainsPlatformReference (AssemblyDefinition assembly)
@ -907,6 +907,11 @@ namespace Registrar {
return type.Resolve ().IsInterface;
}
protected override bool IsAbstract (TypeReference type)
{
return type.Resolve ().IsAbstract;
}
protected override TypeReference[] GetInterfaces (TypeReference type)
{
var td = type.Resolve ();

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

@ -805,16 +805,16 @@ namespace Registrar {
}
}
protected override Exception CreateException (int code, Exception innerException, MethodDefinition method, string message, params object[] args)
protected override Exception CreateExceptionImpl (int code, bool error, Exception innerException, MethodDefinition method, string message, params object[] args)
{
return ErrorHelper.CreateError (App, code, innerException, method, message, args);
return ErrorHelper.Create (App, code, error, innerException, method, message, args);
}
protected override Exception CreateException (int code, Exception innerException, TypeReference type, string message, params object [] args)
protected override Exception CreateExceptionImpl (int code, bool error, Exception innerException, TypeReference type, string message, params object [] args)
{
return ErrorHelper.CreateError (App, code, innerException, type, message, args);
return ErrorHelper.Create (App, code, error, innerException, type, message, args);
}
protected override bool ContainsPlatformReference (AssemblyDefinition assembly)
{
if (assembly.Name.Name == PlatformAssembly)
@ -1099,7 +1099,16 @@ namespace Registrar {
protected override bool IsInterface (TypeReference type)
{
return type.Resolve ().IsInterface;
if (type.IsArray)
return false;
return type.Resolve ()?.IsInterface == true;
}
protected override bool IsAbstract (TypeReference type)
{
if (type.IsArray)
return false;
return type.Resolve ()?.IsAbstract == true;
}
protected override TypeReference[] GetInterfaces (TypeReference type)