[object][processor] Filter and warn about unsupported C# features/types (#117)

This allow the tool to produce something on existing binaries, which
will make testing easier as we go forward.
This commit is contained in:
Sebastien Pouliot 2017-04-13 15:21:39 -04:00 коммит произвёл GitHub
Родитель 4c60ece830
Коммит 2f52100568
5 изменённых файлов: 257 добавлений и 65 удалений

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

@ -48,21 +48,81 @@ The tool could not find the currently selected Xcode location using the `xcode-s
The architecture in the error message is not valid for the targeted platform. Please verify that the --abi option is passed a valid architecture.
<h3><a name="EM0009"/>EM0009: The feature `X` is not currently implemented by the generator</h3>
This is a known issue that we intend to fix in a future release of the generator. Contributions are welcome.
<h3><a name="EM0099"/>EM0099: Internal error *. Please file a bug report with a test case (https://github.com/mono/Embeddinator-4000/issues).</h3>
This error message is reported when an internal consistency check in the Embeddinator-4000 fails.
This indicates a bug in the Embeddinator-4000; please file a bug report at [https://github.com/mono/Embeddinator-4000/issues](https://github.com/mono/Embeddinator-4000/issues) with a test case.
# EM1xxx: code generation
<h3><a name="EM1000"/>EM1000: The feature `X` is not currently implemented by the generator</h3>
<!-- 1xxx: code processing -->
This is a known issue that we intend to fix in a future release of the generator. Contributions are welcome.
# EM1xxx: Code Processing
<h3><a name="EM1010"/>Type `T` is not generated because `X` are not supported.</h3>
This is a **warning** that the type `T` will be ignored (i.e. nothing will be generated) because it uses `X`, a feature that is not supported.
Note: Supported features will evolve with new versions of the tool.
<h3><a name="EM1011"/>Type `T` is not generated because it lacks a native counterpart.</h3>
This is a **warning** that the type `T` will be ignored (i.e. nothing will be generated) because it uses it expose something from the .NET framework that has no counterpart in the native platform.
<h3><a name="EM1011"/>Type `T` is not generated because it lacks marshaling code with a native counterpart.</h3>
This is a **warning** that the type `T` will be ignored (i.e. nothing will be generated) because it uses it expose something from the .NET framework that requires extra marshaling.
Note: This is something that is might get supported, with some limitations, in a future version of the tool.
<h3><a name="EM1020"/>Constructor `C` is not generated because of parameter type `T` is not supported.</h3>
This is a **warning** that the constructor `C` will be ignored (i.e. nothing will be generated) because a parameter of type `T` is not supported.
There should be an earlier warning giving more information why type `T` is not supported.
Note: Supported features will evolve with new versions of the tool.
<h3><a name="EM1030"/>Method `M` is not generated because return type `T` is not supported.</h3>
This is a **warning** that the method `M` will be ignored (i.e. nothing will be generated) because it's return type `T` is not supported.
There should be an earlier warning giving more information why type `T` is not supported.
Note: Supported features will evolve with new versions of the tool.
<h3><a name="EM1031"/>Method `M` is not generated because of parameter type `T` is not supported.</h3>
This is a **warning** that the method `M` will be ignored (i.e. nothing will be generated) because a parameter of type `T` is not supported.
There should be an earlier warning giving more information why type `T` is not supported.
Note: Supported features will evolve with new versions of the tool.
<h3><a name="EM1040"/>Property `P` is not generated because of parameter type `T` is not supported.</h3>
This is a **warning** that the property `P` will be ignored (i.e. nothing will be generated) because the exposed type `T` is not supported.
There should be an earlier warning giving more information why type `T` is not supported.
Note: Supported features will evolve with new versions of the tool.
<!-- 2xxx: code generation -->
# EM2xxx: Code Generation
<!-- 1xxx: code generation -->
<!-- 2xxx: reserved -->
<!-- 3xxx: reserved -->
<!-- 4xxx: reserved -->
<!-- 5xxx: reserved -->

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

@ -107,7 +107,7 @@ namespace Embeddinator {
Console.WriteLine ("Done");
return result;
} catch (NotImplementedException e) {
throw new EmbeddinatorException (1000, $"The feature `{e.Message}` is not currently supported by the tool");
throw new EmbeddinatorException (9, $"The feature `{e.Message}` is not currently supported by the tool");
}
default:
throw ErrorHelper.CreateError (99, "Internal error: invalid action {0}. Please file a bug report with a test case (https://github.com/mono/Embeddinator-4000/issues)", action);

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

@ -307,6 +307,7 @@
<Compile Include="ErrorHelper.cs" />
<Compile Include="Version.generated.cs" />
<Compile Include="objcgenerator-helpers.cs" />
<Compile Include="objcgenerator-processor.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="support\" />

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

@ -0,0 +1,183 @@
using System;
using System.Collections.Generic;
using System.Linq;
using IKVM.Reflection;
using Type = IKVM.Reflection.Type;
using Embeddinator;
namespace ObjC {
public partial class ObjCGenerator {
List<Exception> delayed = new List<Exception> ();
HashSet<Type> unsupported = new HashSet<Type> ();
bool IsSupported (Type t)
{
if (t.IsByRef)
return IsSupported (t.GetElementType ());
if (unsupported.Contains (t))
return false;
// FIXME enums
if (t.IsEnum) {
delayed.Add (ErrorHelper.CreateWarning (1010, $"Type `{t}` is not generated because `enums` are not supported."));
unsupported.Add (t);
return false;
}
// FIXME protocols
if (t.IsInterface) {
delayed.Add (ErrorHelper.CreateWarning (1010, $"Type `{t}` is not generated because `interfaces` are not supported."));
unsupported.Add (t);
return false;
}
if (t.IsGenericParameter || t.IsGenericType) {
delayed.Add (ErrorHelper.CreateWarning (1010, $"Type `{t}` is not generated because `generics` are not supported."));
unsupported.Add (t);
return false;
}
switch (t.Namespace) {
case "System":
switch (t.Name) {
case "Object": // we cannot accept arbitrary NSObject (which we might not have bound) into mono
case "Exception":
case "IFormatProvider":
case "Type":
delayed.Add (ErrorHelper.CreateWarning (1011, $"Type `{t}` is not generated because it lacks a native counterpart."));
unsupported.Add (t);
return false;
case "DateTime": // FIXME: NSDateTime
case "Decimal": // FIXME: NSDecimal
case "TimeSpan":
delayed.Add (ErrorHelper.CreateWarning (1012, $"Type `{t}` is not generated because it lacks a marshaling code with a native counterpart."));
unsupported.Add (t);
return false;
}
break;
}
return true;
}
protected IEnumerable<Type> GetTypes (Assembly a)
{
foreach (var t in a.GetTypes ()) {
if (!t.IsPublic)
continue;
if (!IsSupported (t))
continue;
yield return t;
}
}
protected IEnumerable<ConstructorInfo> GetConstructors (Type t)
{
foreach (var ctor in t.GetConstructors ()) {
// .cctor not to be called directly by native code
if (ctor.IsStatic)
continue;
if (!ctor.IsPublic)
continue;
bool pcheck = true;
foreach (var p in ctor.GetParameters ()) {
var pt = p.ParameterType;
if (!IsSupported (pt)) {
delayed.Add (ErrorHelper.CreateWarning (1020, $"Constructor `{ctor}` is not generated because of parameter type `{pt}` is not supported."));
pcheck = false;
}
}
if (!pcheck)
continue;
yield return ctor;
}
}
protected IEnumerable<MethodInfo> GetMethods (Type t)
{
foreach (var mi in t.GetMethods (BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly)) {
if (!mi.IsPublic)
continue;
var rt = mi.ReturnType;
if (!IsSupported (rt)) {
delayed.Add (ErrorHelper.CreateWarning (1030, $"Method `{mi}` is not generated because return type `{rt}` is not supported."));
continue;
}
bool pcheck = true;
foreach (var p in mi.GetParameters ()) {
var pt = p.ParameterType;
if (!IsSupported (pt)) {
delayed.Add (ErrorHelper.CreateWarning (1031, $"Method `{mi}` is not generated because of parameter type `{pt}` is not supported."));
pcheck = false;
}
}
if (!pcheck)
continue;
yield return mi;
}
}
protected IEnumerable<PropertyInfo> GetProperties (Type t)
{
foreach (var pi in t.GetProperties (BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly)) {
var pt = pi.PropertyType;
if (!IsSupported (pt)) {
delayed.Add (ErrorHelper.CreateWarning (1040, $"Property `{pi}` is not generated because of parameter type `{pt}` is not supported."));
continue;
}
yield return pi;
}
}
List<Type> types = new List<Type> ();
Dictionary<Type, List<ConstructorInfo>> ctors = new Dictionary<Type, List<ConstructorInfo>> ();
Dictionary<Type, List<MethodInfo>> methods = new Dictionary<Type, List<MethodInfo>> ();
Dictionary<Type, List<PropertyInfo>> properties = new Dictionary<Type, List<PropertyInfo>> ();
public override void Process (IEnumerable<Assembly> assemblies)
{
foreach (var a in assemblies) {
foreach (var t in GetTypes (a)) {
// gather types for forward declarations
types.Add (t);
var constructors = GetConstructors (t).OrderBy ((arg) => arg.ParameterCount).ToList ();
ctors.Add (t, constructors);
var meths = GetMethods (t).OrderBy ((arg) => arg.Name).ToList ();
methods.Add (t, meths);
var props = new List<PropertyInfo> ();
foreach (var pi in GetProperties (t)) {
var getter = pi.GetGetMethod ();
var setter = pi.GetSetMethod ();
// setter only property are valid in .NET and we need to generate a method in ObjC (there's no writeonly properties)
if (getter == null)
continue;
// we can do better than methods for the more common cases (readonly and readwrite)
meths.Remove (getter);
meths.Remove (setter);
props.Add (pi);
}
props = props.OrderBy ((arg) => arg.Name).ToList ();
properties.Add (t, props);
}
}
types = types.OrderBy ((arg) => arg.FullName).OrderBy ((arg) => types.Contains (arg.BaseType)).ToList ();
Console.WriteLine ($"\t{types.Count} types found");
ErrorHelper.Show (delayed);
}
}
}

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

@ -16,60 +16,6 @@ namespace ObjC {
static TextWriter headers = new StringWriter ();
static TextWriter implementation = new StringWriter ();
static ParameterInfo [] NoParameters = new ParameterInfo [0];
List<Type> types = new List<Type> ();
Dictionary<Type, List<ConstructorInfo>> ctors = new Dictionary<Type, List<ConstructorInfo>> ();
Dictionary<Type, List<MethodInfo>> methods = new Dictionary<Type, List<MethodInfo>> ();
Dictionary<Type, List<PropertyInfo>> properties = new Dictionary<Type, List<PropertyInfo>> ();
public override void Process (IEnumerable<Assembly> assemblies)
{
foreach (var a in assemblies) {
foreach (var t in a.GetTypes ()) {
if (!t.IsPublic)
continue;
// gather types for forward declarations
types.Add (t);
var constructors = new List<ConstructorInfo> ();
foreach (var ctor in t.GetConstructors ()) {
// .cctor not to be called directly by native code
if (ctor.IsStatic)
continue;
if (!ctor.IsPublic)
continue;
constructors.Add (ctor);
}
constructors = constructors.OrderBy ((arg) => arg.ParameterCount).ToList ();
ctors.Add (t, constructors);
var meths = new List<MethodInfo> ();
foreach (var mi in t.GetMethods (BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly)) {
meths.Add (mi);
}
methods.Add (t, meths);
var props = new List<PropertyInfo> ();
foreach (var pi in t.GetProperties (BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly)) {
var getter = pi.GetGetMethod ();
var setter = pi.GetSetMethod ();
// setter only property are valid in .NET and we need to generate a method in ObjC (there's no writeonly properties)
if (getter == null)
continue;
// we can do better than methods for the more common cases (readonly and readwrite)
meths.Remove (getter);
meths.Remove (setter);
props.Add (pi);
}
props = props.OrderBy ((arg) => arg.Name).ToList ();
properties.Add (t, props);
}
}
types = types.OrderBy ((arg) => arg.FullName).OrderBy ((arg) => types.Contains (arg.BaseType)).ToList ();
Console.WriteLine ($"\t{types.Count} types found");
}
public override void Generate (IEnumerable<Assembly> assemblies)
{
headers.WriteLine ("#include \"embeddinator.h\"");
@ -340,7 +286,11 @@ namespace ObjC {
implementation.WriteLine ($"\t\t__args [{i}] = &{p.Name};");
break;
default:
throw new NotImplementedException ($"Converting type {p.ParameterType.FullName} to mono code");
if (types.Contains (pt))
implementation.WriteLine ($"\t\t__args [{i}] = {p.Name};");
else
throw new NotImplementedException ($"Converting type {pt.FullName} to mono code");
break;
}
}
postInvoke = post.ToString ();
@ -569,15 +519,13 @@ namespace ObjC {
switch (t.Namespace) {
case "System":
switch (t.Name) {
case "Object":
return "object";
case "Void":
return "void";
default:
throw new NotImplementedException ($"Converting type {t.Name} to a mono type name");
return "object";
}
default:
throw new NotImplementedException ($"Converting type {t.Name} to a mono type name");
return "object";
}
case TypeCode.Boolean:
return "bool";