8608 строки
324 KiB
C#
8608 строки
324 KiB
C#
//
|
|
// This is the binding generator for the MonoTouch API, it uses the
|
|
// contract in API.cs to generate the binding.
|
|
//
|
|
// Authors:
|
|
// Geoff Norton
|
|
// Miguel de Icaza
|
|
// Marek Safar (marek.safar@gmail.com)
|
|
//
|
|
// Copyright 2009-2010, Novell, Inc.
|
|
// Copyright 2011-2015 Xamarin, Inc.
|
|
//
|
|
//
|
|
// This generator produces various */*.g.cs files based on the
|
|
// interface-based type description on this file, see the
|
|
// embedded `MonoTouch.UIKit' namespace here for an example
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining
|
|
// a copy of this software and associated documentation files (the
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
|
// permit persons to whom the Software is furnished to do so, subject to
|
|
// the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be
|
|
// included in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
//
|
|
// TODO:
|
|
// * Add support for wrapping "ref" and "out" NSObjects (WrappedTypes)
|
|
// Typically this is necessary for things like NSError.
|
|
//
|
|
|
|
using System;
|
|
using System.Linq;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.Text;
|
|
using System.ComponentModel;
|
|
using System.Reflection;
|
|
|
|
using ObjCRuntime;
|
|
using Foundation;
|
|
using Xamarin.Utils;
|
|
|
|
public static class GeneratorExtensions
|
|
{
|
|
public static StreamWriter Write (this StreamWriter sw, char c, int count)
|
|
{
|
|
for (int i = 0; i < count; i++)
|
|
sw.Write (c);
|
|
return sw;
|
|
}
|
|
}
|
|
|
|
public static class ReflectionExtensions {
|
|
public static BaseTypeAttribute GetBaseTypeAttribute (Type type, Generator generator)
|
|
{
|
|
return generator.AttributeManager.GetCustomAttribute<BaseTypeAttribute> (type);
|
|
}
|
|
|
|
public static Type GetBaseType (Type type, Generator generator)
|
|
{
|
|
BaseTypeAttribute bta = GetBaseTypeAttribute (type, generator);
|
|
Type base_type = bta != null ? bta.BaseType : generator.TypeManager.System_Object;
|
|
|
|
return base_type;
|
|
}
|
|
|
|
public static List <PropertyInfo> GatherProperties (this Type type, Generator generator) {
|
|
return type.GatherProperties (BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static, generator);
|
|
}
|
|
|
|
//
|
|
// Returns true if the specified method info or property info is not
|
|
// available in the current platform (because it has the attribute
|
|
// [Unavailable (ThisPlatform) or because the shorthand versions
|
|
// of [NoiOS] or [NoMac] are applied.
|
|
//
|
|
// This needs to merge, because we might have multiple attributes in
|
|
// use, for example, the availability (iOS (7,0)) and the fact that this
|
|
// is not available on Mac (NoMac).
|
|
//
|
|
public static bool IsUnavailable (this ICustomAttributeProvider provider, Generator generator)
|
|
{
|
|
var attributes = generator.AttributeManager.GetCustomAttributes<AvailabilityBaseAttribute> (provider);
|
|
var platform = generator.CurrentPlatform;
|
|
return IsUnavailable (attributes, platform);
|
|
}
|
|
|
|
public static bool IsUnavailable (AvailabilityBaseAttribute[] attributes, PlatformName platform)
|
|
{
|
|
if (attributes.Any (attr => attr.AvailabilityKind == AvailabilityKind.Unavailable && attr.Platform == platform))
|
|
return true;
|
|
|
|
if (platform == PlatformName.MacCatalyst) {
|
|
// If we're targetting Mac Catalyst, and we don't have any availability information for Mac Catalyst,
|
|
// then use the availability for iOS
|
|
var anyCatalyst = attributes.Any (v => v.Platform == PlatformName.MacCatalyst);
|
|
if (!anyCatalyst)
|
|
return IsUnavailable (attributes, PlatformName.iOS);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public static AvailabilityBaseAttribute GetAvailability (this ICustomAttributeProvider attrProvider, AvailabilityKind availabilityKind, Generator generator)
|
|
{
|
|
return generator.AttributeManager.GetCustomAttributes<AvailabilityBaseAttribute> (attrProvider)
|
|
.FirstOrDefault (attr =>
|
|
attr.AvailabilityKind == availabilityKind &&
|
|
attr.Platform == generator.CurrentPlatform
|
|
);
|
|
}
|
|
|
|
public static List <PropertyInfo> GatherProperties (this Type type, BindingFlags flags, Generator generator) {
|
|
List <PropertyInfo> properties = new List <PropertyInfo> (type.GetProperties (flags));
|
|
|
|
if (generator.IsPublicMode)
|
|
return properties;
|
|
|
|
Type parent_type = GetBaseType (type, generator);
|
|
string owrap;
|
|
string nwrap;
|
|
|
|
if (parent_type != generator.TypeManager.NSObject) {
|
|
if (generator.AttributeManager.HasAttribute<ModelAttribute> (parent_type)) {
|
|
foreach (PropertyInfo pinfo in parent_type.GetProperties (flags)) {
|
|
bool toadd = true;
|
|
var modelea = generator.GetExportAttribute (pinfo, out nwrap);
|
|
|
|
if (modelea == null)
|
|
continue;
|
|
|
|
foreach (PropertyInfo exists in properties) {
|
|
var origea = generator.GetExportAttribute (exists, out owrap);
|
|
if (origea.Selector == modelea.Selector)
|
|
toadd = false;
|
|
}
|
|
|
|
if (toadd)
|
|
properties.Add (pinfo);
|
|
}
|
|
}
|
|
}
|
|
|
|
return properties;
|
|
}
|
|
|
|
public static List <MethodInfo> GatherMethods (this Type type, Generator generator) {
|
|
return type.GatherMethods (BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static, generator);
|
|
}
|
|
|
|
public static bool IsInternal (this MemberInfo mi, Generator generator)
|
|
{
|
|
return generator.AttributeManager.HasAttribute<InternalAttribute> (mi)
|
|
|| (generator.AttributeManager.HasAttribute<UnifiedInternalAttribute> (mi));
|
|
}
|
|
|
|
public static bool IsUnifiedInternal (this MemberInfo mi, Generator generator)
|
|
{
|
|
return (generator.AttributeManager.HasAttribute<UnifiedInternalAttribute> (mi));
|
|
}
|
|
|
|
public static bool IsInternal (this PropertyInfo pi, Generator generator)
|
|
{
|
|
return generator.AttributeManager.HasAttribute<InternalAttribute> (pi)
|
|
|| (generator.AttributeManager.HasAttribute<UnifiedInternalAttribute> (pi));
|
|
}
|
|
|
|
public static bool IsInternal (this Type type, Generator generator)
|
|
{
|
|
return generator.AttributeManager.HasAttribute<InternalAttribute> (type)
|
|
|| (generator.AttributeManager.HasAttribute<UnifiedInternalAttribute> (type));
|
|
}
|
|
|
|
public static List <MethodInfo> GatherMethods (this Type type, BindingFlags flags, Generator generator) {
|
|
List <MethodInfo> methods = new List <MethodInfo> (type.GetMethods (flags));
|
|
|
|
if (generator.IsPublicMode)
|
|
return methods;
|
|
|
|
Type parent_type = GetBaseType (type, generator);
|
|
|
|
if (parent_type != generator.TypeManager.NSObject) {
|
|
if (generator.AttributeManager.HasAttribute<ModelAttribute> (parent_type))
|
|
foreach (MethodInfo minfo in parent_type.GetMethods ())
|
|
if (generator.AttributeManager.HasAttribute<ExportAttribute> (minfo))
|
|
methods.Add (minfo);
|
|
}
|
|
|
|
return methods;
|
|
}
|
|
}
|
|
|
|
// Fixes bug 27430 - btouch doesn't escape identifiers with the same name as C# keywords
|
|
public static class StringExtensions
|
|
{
|
|
public static string GetSafeParamName (this string paramName)
|
|
{
|
|
if (paramName == null)
|
|
return paramName;
|
|
|
|
return IsValidIdentifier (paramName) ? paramName : "@" + paramName;
|
|
}
|
|
|
|
// Since we're building against the iOS assemblies and there's no code generation there,
|
|
// I'm bringing the implementation from:
|
|
// mono/mcs/class//System/Microsoft.CSharp/CSharpCodeGenerator.cs
|
|
static bool IsValidIdentifier (string identifier)
|
|
{
|
|
if (identifier == null || identifier.Length == 0)
|
|
return false;
|
|
|
|
if (keywordsTable == null)
|
|
FillKeywordTable ();
|
|
|
|
if (keywordsTable.Contains (identifier))
|
|
return false;
|
|
|
|
if (!is_identifier_start_character (identifier [0]))
|
|
return false;
|
|
|
|
for (int i = 1; i < identifier.Length; i ++)
|
|
if (! is_identifier_part_character (identifier [i]))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool is_identifier_start_character (char c)
|
|
{
|
|
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '@' || Char.IsLetter (c);
|
|
}
|
|
|
|
static bool is_identifier_part_character (char c)
|
|
{
|
|
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || (c >= '0' && c <= '9') || Char.IsLetter (c);
|
|
}
|
|
|
|
static void FillKeywordTable ()
|
|
{
|
|
lock (keywords) {
|
|
if (keywordsTable == null) {
|
|
keywordsTable = new Hashtable ();
|
|
foreach (string keyword in keywords) {
|
|
keywordsTable.Add (keyword, keyword);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static Hashtable keywordsTable;
|
|
private static string[] keywords = new string[] {
|
|
"abstract","event","new","struct","as","explicit","null","switch","base","extern",
|
|
"this","false","operator","throw","break","finally","out","true",
|
|
"fixed","override","try","case","params","typeof","catch","for",
|
|
"private","foreach","protected","checked","goto","public",
|
|
"unchecked","class","if","readonly","unsafe","const","implicit","ref",
|
|
"continue","in","return","using","virtual","default",
|
|
"interface","sealed","volatile","delegate","internal","do","is",
|
|
"sizeof","while","lock","stackalloc","else","static","enum",
|
|
"namespace",
|
|
"object","bool","byte","float","uint","char","ulong","ushort",
|
|
"decimal","int","sbyte","short","double","long","string","void",
|
|
"partial", "yield", "where"
|
|
};
|
|
}
|
|
|
|
//
|
|
// Used to encapsulate flags about types in either the parameter or the return value
|
|
// For now, it only supports the [PlainString] attribute on strings.
|
|
//
|
|
public class MarshalInfo {
|
|
public Generator Generator;
|
|
public bool PlainString;
|
|
public Type Type;
|
|
public bool IsOut;
|
|
|
|
// This is set on a string parameter if the argument parameters are set to
|
|
// Copy. This means that we can do fast string passing.
|
|
public bool ZeroCopyStringMarshal;
|
|
|
|
public bool IsAligned;
|
|
|
|
// Used for parameters
|
|
public MarshalInfo (Generator generator, MethodInfo mi, ParameterInfo pi)
|
|
{
|
|
this.Generator = generator;
|
|
PlainString = Generator.AttributeManager.HasAttribute<PlainStringAttribute> (pi);
|
|
Type = pi.ParameterType;
|
|
ZeroCopyStringMarshal = (Type == Generator.TypeManager.System_String) && PlainString == false && !Generator.AttributeManager.HasAttribute<DisableZeroCopyAttribute> (pi) && generator.type_wants_zero_copy;
|
|
if (ZeroCopyStringMarshal && Generator.AttributeManager.HasAttribute<DisableZeroCopyAttribute> (mi))
|
|
ZeroCopyStringMarshal = false;
|
|
IsOut = TypeManager.IsOutParameter (pi);
|
|
}
|
|
|
|
// Used to return values
|
|
public MarshalInfo (Generator generator, MethodInfo mi)
|
|
{
|
|
this.Generator = generator;
|
|
PlainString = Generator.AttributeManager.HasAttribute<PlainStringAttribute> (AttributeManager.GetReturnTypeCustomAttributes (mi));
|
|
Type = mi.ReturnType;
|
|
}
|
|
}
|
|
|
|
public class Tuple<A,B> {
|
|
public Tuple (A a, B b)
|
|
{
|
|
Item1 = a;
|
|
Item2 = b;
|
|
}
|
|
public A Item1;
|
|
public B Item2;
|
|
}
|
|
//
|
|
// Encapsulates the information necessary to create a block delegate
|
|
//
|
|
// FIXME: We do not really need this class, we should just move all this
|
|
// pre-processing to the generation stage, instead of decoupling it in two places.
|
|
//
|
|
// The Name is the internal generated name we use for the delegate
|
|
// The Parameters is used for the internal delegate signature
|
|
// The Invoke contains the invocation steps necessary to invoke the method
|
|
//
|
|
public class TrampolineInfo {
|
|
public string UserDelegate, DelegateName, TrampolineName, Parameters, Convert, Invoke, ReturnType, DelegateReturnType, ReturnFormat, Clear, OutReturnType, PostConvert;
|
|
public string UserDelegateTypeAttribute;
|
|
public Type Type;
|
|
|
|
public TrampolineInfo (string userDelegate, string delegateName, string trampolineName, string pars, string convert, string invoke, string returnType, string delegateReturnType, string returnFormat, string clear, string postConvert, Type type)
|
|
{
|
|
UserDelegate = userDelegate;
|
|
DelegateName = delegateName;
|
|
Parameters = pars;
|
|
TrampolineName = trampolineName;
|
|
Convert = convert;
|
|
Invoke = invoke;
|
|
ReturnType = returnType;
|
|
DelegateReturnType = delegateReturnType;
|
|
ReturnFormat = returnFormat;
|
|
Clear = clear;
|
|
PostConvert = postConvert;
|
|
this.Type = type;
|
|
|
|
TrampolineName = "Invoke";
|
|
}
|
|
|
|
// Name for the static class generated that contains the Objective-C to C# block bridge
|
|
public string StaticName {
|
|
get {
|
|
return "S" + DelegateName;
|
|
}
|
|
}
|
|
|
|
// Name for the class generated that allows C# to invoke an Objective-C block
|
|
public string NativeInvokerName {
|
|
get {
|
|
return "NI" + DelegateName;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// This class is used to generate a graph of the type hierarchy of the
|
|
// generated types and required by the UIApperance support to determine
|
|
// which types need to have Appearance methods created
|
|
//
|
|
public class GeneratedType {
|
|
public GeneratedType (Type t, GeneratedTypes root)
|
|
{
|
|
Root = root;
|
|
Type = t;
|
|
var generator = root.Generator;
|
|
foreach (var iface in Type.GetInterfaces ()){
|
|
if (iface.Name == "UIAppearance" || iface.Name == "IUIAppearance")
|
|
ImplementsAppearance = true;
|
|
}
|
|
var btype = ReflectionExtensions.GetBaseType (Type, generator);
|
|
if (btype != generator.TypeManager.System_Object){
|
|
Parent = btype;
|
|
// protected against a StackOverflowException - bug #19751
|
|
// it does not protect against large cycles (but good against copy/paste errors)
|
|
if (Parent == Type)
|
|
throw new BindingException (1030, true, Type, Parent);
|
|
ParentGenerated = Root.Lookup (Parent);
|
|
|
|
// If our parent had UIAppearance, we flag this class as well
|
|
if (ParentGenerated.ImplementsAppearance)
|
|
ImplementsAppearance = true;
|
|
ParentGenerated.Children.Add (this);
|
|
}
|
|
|
|
if (generator.AttributeManager.HasAttribute<CategoryAttribute> (t))
|
|
ImplementsAppearance = false;
|
|
}
|
|
public GeneratedTypes Root;
|
|
public Type Type;
|
|
public List<GeneratedType> Children = new List<GeneratedType> (1);
|
|
public Type Parent;
|
|
public GeneratedType ParentGenerated;
|
|
public bool ImplementsAppearance;
|
|
|
|
List<MemberInfo> appearance_selectors;
|
|
|
|
public List<MemberInfo> AppearanceSelectors {
|
|
get {
|
|
if (appearance_selectors == null)
|
|
appearance_selectors = new List<MemberInfo> ();
|
|
return appearance_selectors;
|
|
}
|
|
}
|
|
}
|
|
|
|
public class GeneratedTypes
|
|
{
|
|
public Generator Generator;
|
|
|
|
Dictionary<Type, GeneratedType> knownTypes = new Dictionary<Type, GeneratedType> ();
|
|
|
|
public GeneratedTypes (Generator generator)
|
|
{
|
|
this.Generator = generator;
|
|
}
|
|
|
|
public GeneratedType Lookup (Type t)
|
|
{
|
|
if (knownTypes.ContainsKey (t))
|
|
return knownTypes [t];
|
|
var n = new GeneratedType (t, this);
|
|
knownTypes [t] = n;
|
|
return n;
|
|
}
|
|
}
|
|
|
|
public interface IMemberGatherer {
|
|
IEnumerable<MethodInfo> GetTypeContractMethods (Type source);
|
|
}
|
|
|
|
class WrapPropMemberInformation
|
|
{
|
|
public bool HasWrapOnGetter { get => WrapGetter != null; }
|
|
public bool HasWrapOnSetter { get => WrapSetter != null; }
|
|
public string WrapGetter { get; private set; }
|
|
public string WrapSetter { get; private set; }
|
|
|
|
public WrapPropMemberInformation (PropertyInfo pi, Generator generator)
|
|
{
|
|
WrapGetter = generator.AttributeManager.GetCustomAttribute<WrapAttribute> (pi?.GetMethod)?.MethodName;
|
|
WrapSetter = generator.AttributeManager.GetCustomAttribute<WrapAttribute> (pi?.SetMethod)?.MethodName;
|
|
}
|
|
}
|
|
|
|
|
|
public class MemberInformation
|
|
{
|
|
Generator Generator;
|
|
AttributeManager AttributeManager { get { return Generator.AttributeManager; } }
|
|
public readonly MemberInfo mi;
|
|
public readonly Type type;
|
|
public readonly Type category_extension_type;
|
|
internal readonly WrapPropMemberInformation wpmi;
|
|
public readonly bool is_abstract, is_protected, is_internal, is_unified_internal, is_override, is_new, is_sealed, is_static, is_thread_static, is_autorelease, is_wrapper, is_forced;
|
|
public readonly bool ignore_category_static_warnings, is_basewrapper_protocol_method;
|
|
public readonly bool has_inner_wrap_attribute;
|
|
public readonly Generator.ThreadCheck threadCheck;
|
|
public bool is_unsafe, is_virtual_method, is_export, is_category_extension, is_variadic, is_interface_impl, is_extension_method, is_appearance, is_model, is_ctor;
|
|
public bool is_return_release;
|
|
public bool is_type_sealed;
|
|
public bool protocolize;
|
|
public string selector, wrap_method, is_forced_owns;
|
|
public bool is_bindAs => Generator.HasBindAsAttribute (mi);
|
|
|
|
public MethodInfo method { get { return (MethodInfo) mi; } }
|
|
public PropertyInfo property { get { return (PropertyInfo) mi; } }
|
|
|
|
MemberInformation (Generator generator, IMemberGatherer gather, MemberInfo mi, Type type, bool is_interface_impl, bool is_extension_method, bool is_appearance, bool is_model)
|
|
{
|
|
Generator = generator;
|
|
var method = mi as MethodInfo;
|
|
|
|
is_ctor = mi is MethodInfo && mi.Name == "Constructor";
|
|
is_abstract = AttributeManager.HasAttribute<AbstractAttribute> (mi) && mi.DeclaringType == type;
|
|
is_protected = AttributeManager.HasAttribute<ProtectedAttribute> (mi);
|
|
is_internal = mi.IsInternal (generator);
|
|
is_unified_internal = AttributeManager.HasAttribute<UnifiedInternalAttribute> (mi);
|
|
is_override = AttributeManager.HasAttribute<OverrideAttribute> (mi) || !Generator.MemberBelongsToType (mi.DeclaringType, type);
|
|
is_new = AttributeManager.HasAttribute<NewAttribute> (mi);
|
|
is_sealed = AttributeManager.HasAttribute<SealedAttribute> (mi);
|
|
is_static = AttributeManager.HasAttribute<StaticAttribute> (mi);
|
|
is_thread_static = AttributeManager.HasAttribute<IsThreadStaticAttribute> (mi);
|
|
is_autorelease = AttributeManager.HasAttribute<AutoreleaseAttribute> (mi);
|
|
is_wrapper = !AttributeManager.HasAttribute<SyntheticAttribute> (mi.DeclaringType);
|
|
is_type_sealed = AttributeManager.HasAttribute<SealedAttribute> (mi.DeclaringType);
|
|
is_return_release = method != null && AttributeManager.HasAttribute<ReleaseAttribute> (AttributeManager.GetReturnTypeCustomAttributes (method));
|
|
is_forced = Generator.HasForcedAttribute (mi, out is_forced_owns);
|
|
|
|
var tsa = AttributeManager.GetCustomAttribute<ThreadSafeAttribute> (mi);
|
|
// if there's an attribute then it overrides the parent (e.g. type attribute) or namespace default
|
|
if (tsa != null) {
|
|
threadCheck = tsa.Safe ? Generator.ThreadCheck.Off : Generator.ThreadCheck.On;
|
|
} else {
|
|
threadCheck = Generator.ThreadCheck.Default; // will be based on the type decision
|
|
}
|
|
this.is_interface_impl = is_interface_impl;
|
|
this.is_extension_method = is_extension_method;
|
|
this.type = type;
|
|
this.is_appearance = is_appearance;
|
|
this.is_model = is_model;
|
|
this.mi = mi;
|
|
|
|
if (is_interface_impl || is_extension_method || is_type_sealed) {
|
|
is_abstract = false;
|
|
is_virtual_method = false;
|
|
}
|
|
|
|
// To avoid a warning, we should determine whether we should insert a "new" in the
|
|
// declaration. If this is an inlined method, then we need to see if this was
|
|
// also inlined in any of the base classes.
|
|
if (mi.DeclaringType != type){
|
|
for (var baseType = ReflectionExtensions.GetBaseType (type, generator); baseType != null && baseType != Generator.TypeManager.System_Object; baseType = ReflectionExtensions.GetBaseType (baseType, generator)) {
|
|
foreach (var baseMethod in gather.GetTypeContractMethods (baseType)){
|
|
if (baseMethod.DeclaringType != baseType && baseMethod == mi){
|
|
// We found a case, we need to flag it as new.
|
|
is_new = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
public MemberInformation (Generator generator, IMemberGatherer gather, MethodInfo mi, Type type, Type category_extension_type, bool is_interface_impl = false, bool is_extension_method = false, bool is_appearance = false, bool is_model = false, string selector = null, bool isBaseWrapperProtocolMethod = false)
|
|
: this (generator, gather, (MemberInfo) mi, type, is_interface_impl, is_extension_method, is_appearance, is_model)
|
|
{
|
|
is_basewrapper_protocol_method = isBaseWrapperProtocolMethod;
|
|
foreach (ParameterInfo pi in mi.GetParameters ())
|
|
if (pi.ParameterType.IsSubclassOf (Generator.TypeManager.System_Delegate))
|
|
is_unsafe = true;
|
|
|
|
if (!is_unsafe && mi.ReturnType.IsSubclassOf (Generator.TypeManager.System_Delegate))
|
|
is_unsafe = true;
|
|
|
|
if (selector != null) {
|
|
this.selector = selector;
|
|
if (!is_sealed && !is_wrapper) {
|
|
is_export = !is_extension_method;
|
|
is_virtual_method = !is_ctor;
|
|
}
|
|
} else {
|
|
object [] attr = AttributeManager.GetCustomAttributes<ExportAttribute> (mi);
|
|
if (attr.Length != 1) {
|
|
attr = AttributeManager.GetCustomAttributes<BindAttribute> (mi);
|
|
if (attr.Length != 1) {
|
|
attr = AttributeManager.GetCustomAttributes<WrapAttribute> (mi);
|
|
if (attr.Length != 1)
|
|
throw new BindingException (1012, true, type, mi.Name);
|
|
|
|
var wrapAtt = (WrapAttribute) attr [0];
|
|
wrap_method = wrapAtt.MethodName;
|
|
is_virtual_method = wrapAtt.IsVirtual;
|
|
} else {
|
|
BindAttribute ba = (BindAttribute) attr [0];
|
|
this.selector = ba.Selector;
|
|
is_virtual_method = ba.Virtual;
|
|
}
|
|
} else {
|
|
ExportAttribute ea = (ExportAttribute) attr [0];
|
|
this.selector = ea.Selector;
|
|
is_variadic = ea.IsVariadic;
|
|
|
|
if (!is_sealed || !is_wrapper) {
|
|
is_virtual_method = !is_ctor;
|
|
is_export = !is_extension_method;
|
|
}
|
|
}
|
|
}
|
|
|
|
this.category_extension_type = category_extension_type;
|
|
if (category_extension_type != null) {
|
|
is_category_extension = true;
|
|
#if NET
|
|
ignore_category_static_warnings = is_internal || type.IsInternal (generator);
|
|
#else
|
|
ignore_category_static_warnings = is_internal || type.IsInternal (generator) || Generator.AttributeManager.GetCustomAttribute<CategoryAttribute> (type).AllowStaticMembers;
|
|
#endif
|
|
}
|
|
|
|
if (is_static || is_category_extension || is_interface_impl || is_extension_method || is_type_sealed)
|
|
is_virtual_method = false;
|
|
}
|
|
|
|
public MemberInformation (Generator generator, IMemberGatherer gather, PropertyInfo pi, Type type, bool is_interface_impl = false)
|
|
: this (generator, gather, (MemberInfo) pi, type, is_interface_impl, false, false, false)
|
|
{
|
|
if (pi.PropertyType.IsSubclassOf (Generator.TypeManager.System_Delegate))
|
|
is_unsafe = true;
|
|
|
|
var export = Generator.GetExportAttribute (pi, out wrap_method);
|
|
if (export != null)
|
|
selector = export.Selector;
|
|
|
|
if (wrap_method != null) {
|
|
var wrapAtt = Generator.AttributeManager.GetCustomAttribute <WrapAttribute> (pi);
|
|
is_virtual_method = wrapAtt?.IsVirtual ?? false;
|
|
}
|
|
else if (is_interface_impl || is_type_sealed)
|
|
is_virtual_method = false;
|
|
else
|
|
is_virtual_method = !is_static;
|
|
|
|
// Properties can have WrapAttribute on getter/setter so we need to check for this
|
|
// but only if no Export is already found on property level.
|
|
if (export is null) {
|
|
wpmi = new WrapPropMemberInformation (pi, generator);
|
|
has_inner_wrap_attribute = wpmi.HasWrapOnGetter || wpmi.HasWrapOnSetter;
|
|
|
|
// Wrap can only be used either at property level or getter/setter level at a given time.
|
|
if (wrap_method != null && has_inner_wrap_attribute)
|
|
throw new BindingException (1063, true, pi.DeclaringType, pi.Name);
|
|
}
|
|
}
|
|
|
|
public string GetVisibility ()
|
|
{
|
|
if (is_interface_impl || is_extension_method)
|
|
return "public";
|
|
|
|
var mod = is_protected ? "protected" : null;
|
|
mod += is_internal ? "internal" : null;
|
|
if (string.IsNullOrEmpty (mod))
|
|
mod = "public";
|
|
return mod;
|
|
}
|
|
|
|
public string GetModifiers ()
|
|
{
|
|
string mods = "";
|
|
|
|
mods += is_unsafe ? "unsafe " : null;
|
|
mods += is_new ? "new " : "";
|
|
|
|
if (is_sealed) {
|
|
mods += "";
|
|
} else if (is_static || is_category_extension || is_extension_method) {
|
|
mods += "static ";
|
|
} else if (is_abstract) {
|
|
#if NET
|
|
mods += "virtual ";
|
|
#else
|
|
mods += "abstract ";
|
|
#endif
|
|
} else if (is_virtual_method && !is_type_sealed) {
|
|
mods += is_override ? "override " : "virtual ";
|
|
}
|
|
|
|
return mods;
|
|
}
|
|
}
|
|
|
|
public class NamespaceManager
|
|
{
|
|
public BindingTouch BindingTouch;
|
|
PlatformName CurrentPlatform { get { return BindingTouch.CurrentPlatform; } }
|
|
Frameworks Frameworks { get { return BindingTouch.Frameworks; } }
|
|
|
|
// Where user-overrideable messaging may live
|
|
public string ObjCRuntime { get; private set; }
|
|
|
|
public string Messaging { get; private set; }
|
|
|
|
public ICollection<string> StandardNamespaces { get; private set; }
|
|
public ICollection<string> UINamespaces { get; private set; }
|
|
public ICollection<string> ImplicitNamespaces { get; private set; }
|
|
public ICollection<string> NamespacesThatConflictWithTypes { get; private set; }
|
|
|
|
public NamespaceManager (BindingTouch binding_touch, string customObjCRuntimeNS, bool skipSystemDrawing)
|
|
{
|
|
BindingTouch = binding_touch;
|
|
|
|
ObjCRuntime = String.IsNullOrEmpty (customObjCRuntimeNS)
|
|
? "ObjCRuntime"
|
|
: customObjCRuntimeNS;
|
|
|
|
Messaging = ObjCRuntime + ".Messaging";
|
|
|
|
StandardNamespaces = new HashSet<string> {
|
|
"Foundation",
|
|
"ObjCRuntime",
|
|
"CoreGraphics",
|
|
};
|
|
|
|
UINamespaces = new HashSet<string> ();
|
|
if (Frameworks.HaveAppKit)
|
|
UINamespaces.Add ("AppKit");
|
|
if (Frameworks.HaveUIKit)
|
|
UINamespaces.Add ("UIKit");
|
|
if (Frameworks.HaveTwitter)
|
|
UINamespaces.Add ("Twitter");
|
|
if (Frameworks.HaveGameKit && CurrentPlatform != PlatformName.MacOSX && CurrentPlatform != PlatformName.WatchOS)
|
|
UINamespaces.Add ("GameKit");
|
|
if (Frameworks.HaveNewsstandKit)
|
|
UINamespaces.Add ("NewsstandKit");
|
|
if (Frameworks.HaveiAd)
|
|
UINamespaces.Add ("iAd");
|
|
if (Frameworks.HaveQuickLook)
|
|
UINamespaces.Add ("QuickLook");
|
|
if (Frameworks.HaveEventKitUI)
|
|
UINamespaces.Add ("EventKitUI");
|
|
if (Frameworks.HaveAddressBookUI)
|
|
UINamespaces.Add ("AddressBookUI");
|
|
if (Frameworks.HaveMapKit && CurrentPlatform != PlatformName.MacOSX && CurrentPlatform != PlatformName.TvOS && CurrentPlatform != PlatformName.WatchOS)
|
|
UINamespaces.Add ("MapKit");
|
|
if (Frameworks.HaveMessageUI)
|
|
UINamespaces.Add ("MessageUI");
|
|
if (Frameworks.HavePhotosUI)
|
|
UINamespaces.Add ("PhotosUI");
|
|
if (Frameworks.HaveHealthKitUI)
|
|
UINamespaces.Add ("HealthKitUI");
|
|
|
|
ImplicitNamespaces = new HashSet<string> ();
|
|
ImplicitNamespaces.Add ("System");
|
|
ImplicitNamespaces.Add ("System.Runtime.InteropServices");
|
|
ImplicitNamespaces.Add ("System.Diagnostics");
|
|
ImplicitNamespaces.Add ("System.Diagnostics.CodeAnalysis");
|
|
ImplicitNamespaces.Add ("System.ComponentModel");
|
|
#if NET
|
|
ImplicitNamespaces.Add ("System.Runtime.Versioning");
|
|
#endif
|
|
ImplicitNamespaces.Add ("System.Threading.Tasks");
|
|
ImplicitNamespaces.Add ("CoreFoundation");
|
|
ImplicitNamespaces.Add ("Foundation");
|
|
ImplicitNamespaces.Add ("ObjCRuntime");
|
|
ImplicitNamespaces.Add ("CoreGraphics");
|
|
ImplicitNamespaces.Add ("CoreML");
|
|
ImplicitNamespaces.Add ("SceneKit");
|
|
|
|
if (Frameworks.HaveAudioUnit)
|
|
ImplicitNamespaces.Add ("AudioUnit");
|
|
if (Frameworks.HaveContacts)
|
|
ImplicitNamespaces.Add ("Contacts");
|
|
if (Frameworks.HaveCoreAnimation)
|
|
ImplicitNamespaces.Add ("CoreAnimation");
|
|
if (Frameworks.HaveCoreLocation)
|
|
ImplicitNamespaces.Add ("CoreLocation");
|
|
if (Frameworks.HaveCoreVideo)
|
|
ImplicitNamespaces.Add ("CoreVideo");
|
|
if (Frameworks.HaveCoreMedia)
|
|
ImplicitNamespaces.Add ("CoreMedia");
|
|
if (Frameworks.HaveSecurity && CurrentPlatform != PlatformName.WatchOS)
|
|
ImplicitNamespaces.Add ("Security");
|
|
if (Frameworks.HaveAVFoundation)
|
|
ImplicitNamespaces.Add ("AVFoundation");
|
|
if (Frameworks.HaveOpenGL)
|
|
ImplicitNamespaces.Add ("OpenGL");
|
|
#if !NET
|
|
if (Frameworks.HaveQTKit)
|
|
ImplicitNamespaces.Add ("QTKit");
|
|
#endif
|
|
if (Frameworks.HaveAppKit)
|
|
ImplicitNamespaces.Add ("AppKit");
|
|
if (Frameworks.HaveCloudKit && CurrentPlatform != PlatformName.WatchOS && CurrentPlatform != PlatformName.TvOS && CurrentPlatform != PlatformName.iOS)
|
|
ImplicitNamespaces.Add ("CloudKit");
|
|
if (Frameworks.HaveCoreMotion && CurrentPlatform != PlatformName.WatchOS && CurrentPlatform != PlatformName.TvOS && CurrentPlatform != PlatformName.MacOSX)
|
|
ImplicitNamespaces.Add ("CoreMotion");
|
|
if (Frameworks.HaveMapKit && CurrentPlatform != PlatformName.WatchOS && CurrentPlatform != PlatformName.TvOS && CurrentPlatform != PlatformName.MacOSX)
|
|
ImplicitNamespaces.Add ("MapKit");
|
|
if (Frameworks.HaveUIKit)
|
|
ImplicitNamespaces.Add ("UIKit");
|
|
if (Frameworks.HaveNewsstandKit)
|
|
ImplicitNamespaces.Add ("NewsstandKit");
|
|
if (Frameworks.HaveGLKit && CurrentPlatform != PlatformName.WatchOS && CurrentPlatform != PlatformName.MacOSX)
|
|
ImplicitNamespaces.Add ("GLKit");
|
|
if (Frameworks.HaveQuickLook && CurrentPlatform != PlatformName.WatchOS && CurrentPlatform != PlatformName.TvOS && CurrentPlatform != PlatformName.MacOSX)
|
|
ImplicitNamespaces.Add ("QuickLook");
|
|
if (Frameworks.HaveAddressBook)
|
|
ImplicitNamespaces.Add ("AddressBook");
|
|
|
|
if (Frameworks.HaveModelIO)
|
|
ImplicitNamespaces.Add ("ModelIO");
|
|
if (Frameworks.HaveMetal)
|
|
ImplicitNamespaces.Add ("Metal");
|
|
|
|
if (Frameworks.HaveCoreImage)
|
|
ImplicitNamespaces.Add ("CoreImage");
|
|
if (Frameworks.HavePhotos)
|
|
ImplicitNamespaces.Add ("Photos");
|
|
if (Frameworks.HaveMediaPlayer)
|
|
ImplicitNamespaces.Add ("MediaPlayer");
|
|
if (Frameworks.HaveMessages)
|
|
ImplicitNamespaces.Add ("Messages");
|
|
if (Frameworks.HaveGameplayKit)
|
|
ImplicitNamespaces.Add ("GameplayKit");
|
|
if (Frameworks.HaveSpriteKit)
|
|
ImplicitNamespaces.Add ("SpriteKit");
|
|
if (Frameworks.HaveFileProvider)
|
|
ImplicitNamespaces.Add ("FileProvider");
|
|
if (Frameworks.HaveNetworkExtension)
|
|
ImplicitNamespaces.Add ("NetworkExtension");
|
|
if (Frameworks.HaveNetwork)
|
|
ImplicitNamespaces.Add ("Network");
|
|
|
|
// These are both types and namespaces
|
|
NamespacesThatConflictWithTypes = new HashSet<string> {
|
|
"AudioUnit",
|
|
};
|
|
|
|
|
|
if (!skipSystemDrawing)
|
|
ImplicitNamespaces.Add ("System.Drawing");
|
|
}
|
|
}
|
|
|
|
public partial class Frameworks {
|
|
HashSet<string> frameworks;
|
|
readonly PlatformName CurrentPlatform;
|
|
public Frameworks (PlatformName current_platform)
|
|
{
|
|
CurrentPlatform = current_platform;
|
|
}
|
|
|
|
bool GetValue (string framework)
|
|
{
|
|
if (frameworks == null) {
|
|
switch (CurrentPlatform) {
|
|
case PlatformName.iOS:
|
|
frameworks = iosframeworks;
|
|
break;
|
|
case PlatformName.WatchOS:
|
|
frameworks = watchosframeworks;
|
|
break;
|
|
case PlatformName.TvOS:
|
|
frameworks = tvosframeworks;
|
|
break;
|
|
case PlatformName.MacOSX:
|
|
frameworks = macosframeworks;
|
|
break;
|
|
case PlatformName.MacCatalyst:
|
|
frameworks = maccatalystframeworks;
|
|
break;
|
|
default:
|
|
throw new BindingException (1047, CurrentPlatform);
|
|
}
|
|
}
|
|
|
|
return frameworks.Contains (framework);
|
|
}
|
|
}
|
|
|
|
public partial class Generator : IMemberGatherer {
|
|
internal bool IsPublicMode;
|
|
internal static string NativeHandleType;
|
|
NamespaceManager ns;
|
|
BindingTouch BindingTouch;
|
|
Frameworks Frameworks { get { return BindingTouch.Frameworks; } }
|
|
public TypeManager TypeManager { get { return BindingTouch.TypeManager; } }
|
|
public AttributeManager AttributeManager { get { return BindingTouch.AttributeManager; } }
|
|
public GeneratedTypes GeneratedTypes;
|
|
List<Exception> exceptions = new List<Exception> ();
|
|
|
|
Dictionary<Type,IEnumerable<string>> selectors = new Dictionary<Type,IEnumerable<string>> ();
|
|
Dictionary<Type,bool> need_static = new Dictionary<Type,bool> ();
|
|
Dictionary<Type,bool> need_abstract = new Dictionary<Type,bool> ();
|
|
Dictionary<string,int> selector_use = new Dictionary<string, int> ();
|
|
Dictionary<string,string> selector_names = new Dictionary<string,string> ();
|
|
Dictionary<string,string> send_methods = new Dictionary<string,string> ();
|
|
List<MarshalType> marshal_types = new List<MarshalType> ();
|
|
Dictionary<Type,TrampolineInfo> trampolines = new Dictionary<Type,TrampolineInfo> ();
|
|
Dictionary<Type,int> trampolines_generic_versions = new Dictionary<Type,int> ();
|
|
Dictionary<Type,Type> notification_event_arg_types = new Dictionary<Type,Type> ();
|
|
Dictionary<string, string> libraries = new Dictionary<string, string> (); // <LibraryName, libraryPath>
|
|
|
|
List<Tuple<string, ParameterInfo[]>> async_result_types = new List<Tuple <string, ParameterInfo[]>> ();
|
|
HashSet<string> async_result_types_emitted = new HashSet<string> ();
|
|
HashSet<string> types_that_must_always_be_globally_named = new HashSet<string> ();
|
|
|
|
//
|
|
// This contains delegates that are referenced in the source and need to be generated.
|
|
//
|
|
Dictionary<string,MethodInfo> delegate_types = new Dictionary<string,MethodInfo> ();
|
|
|
|
public PlatformName CurrentPlatform { get { return BindingTouch.CurrentPlatform; } }
|
|
|
|
public string ApplicationClassName {
|
|
get {
|
|
switch (CurrentPlatform) {
|
|
case PlatformName.iOS:
|
|
case PlatformName.WatchOS:
|
|
case PlatformName.TvOS:
|
|
case PlatformName.MacCatalyst:
|
|
return "UIApplication";
|
|
case PlatformName.MacOSX:
|
|
return "NSApplication";
|
|
default:
|
|
throw new BindingException (1047, CurrentPlatform);
|
|
}
|
|
}
|
|
}
|
|
|
|
public int XamcoreVersion {
|
|
get {
|
|
#if NET
|
|
return 4;
|
|
#else
|
|
switch (CurrentPlatform) {
|
|
case PlatformName.MacOSX:
|
|
case PlatformName.iOS:
|
|
return 2;
|
|
case PlatformName.TvOS:
|
|
case PlatformName.WatchOS:
|
|
return 3;
|
|
default:
|
|
return 4;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
Type [] types, strong_dictionaries;
|
|
bool debug;
|
|
bool external;
|
|
StreamWriter sw, m;
|
|
int indent;
|
|
|
|
public NamespaceManager NamespaceManager {
|
|
get { return ns; }
|
|
}
|
|
|
|
public class MarshalType {
|
|
public Type Type;
|
|
public string Encoding;
|
|
public string ParameterMarshal;
|
|
public string CreateFromRet;
|
|
public string ClosingCreate;
|
|
|
|
public MarshalType (Type t, string encode = null, string fetch = null, string create = null, string closingCreate = ")")
|
|
{
|
|
Type = t;
|
|
Encoding = encode ?? Generator.NativeHandleType;
|
|
ParameterMarshal = fetch ?? "{0}.Handle";
|
|
if (create == null) {
|
|
CreateFromRet = $"Runtime.GetINativeObject<global::{t.FullName}> (";
|
|
ClosingCreate = ", false)!";
|
|
} else {
|
|
CreateFromRet = create;
|
|
ClosingCreate = closingCreate;
|
|
}
|
|
}
|
|
|
|
//
|
|
// When you use this constructor, the marshaling defaults to:
|
|
// Marshal type like this:
|
|
// Encoding = IntPtr
|
|
// Getting the underlying representation: using the .Handle property
|
|
// Intantiating the object: creates a new object by passing the handle to the type.
|
|
//
|
|
public static implicit operator MarshalType (Type type)
|
|
{
|
|
return new MarshalType (type);
|
|
}
|
|
}
|
|
|
|
public bool LookupMarshal (Type t, out MarshalType res)
|
|
{
|
|
res = null;
|
|
// quick out for common (and easy to detect) cases
|
|
if (t.IsArray || t.IsByRef || t.IsPrimitive)
|
|
return false;
|
|
foreach (var mt in marshal_types){
|
|
// full name is required because some types (e.g. CVPixelBuffer) are now also in core.dll
|
|
if (mt.Type.FullName == t.FullName) {
|
|
res = mt;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// Properties and definitions to support binding third-party Objective-C libraries
|
|
//
|
|
string is_direct_binding_value; // An expression that calculates the IsDirectBinding value. Might not be a constant expression. This will be added to every constructor for a type.
|
|
bool? is_direct_binding; // If a constant value for IsDirectBinding is known, it's stored here. Will be null if no constant value is known.
|
|
|
|
// Whether to use ZeroCopy for strings, defaults to false
|
|
public bool ZeroCopyStrings;
|
|
|
|
public bool BindThirdPartyLibrary { get { return BindingTouch.BindThirdPartyLibrary; } }
|
|
public bool InlineSelectors;
|
|
public string BaseDir { get { return basedir; } set { basedir = value; }}
|
|
string basedir;
|
|
HashSet<string> generated_files = new HashSet<string> ();
|
|
|
|
string CoreImageMap {
|
|
get {
|
|
switch (CurrentPlatform) {
|
|
case PlatformName.iOS:
|
|
case PlatformName.WatchOS:
|
|
case PlatformName.TvOS:
|
|
case PlatformName.MacCatalyst:
|
|
return "CoreImage";
|
|
case PlatformName.MacOSX:
|
|
return "Quartz";
|
|
default:
|
|
throw new BindingException (1047, CurrentPlatform);
|
|
}
|
|
}
|
|
}
|
|
|
|
string CoreServicesMap {
|
|
get {
|
|
switch (CurrentPlatform) {
|
|
case PlatformName.iOS:
|
|
case PlatformName.WatchOS:
|
|
case PlatformName.TvOS:
|
|
case PlatformName.MacCatalyst:
|
|
return "MobileCoreServices";
|
|
case PlatformName.MacOSX:
|
|
return "CoreServices";
|
|
default:
|
|
throw new BindingException (1047, CurrentPlatform);
|
|
}
|
|
}
|
|
}
|
|
|
|
string PDFKitMap {
|
|
get {
|
|
switch (CurrentPlatform) {
|
|
case PlatformName.iOS:
|
|
case PlatformName.MacCatalyst:
|
|
return "PDFKit";
|
|
case PlatformName.MacOSX:
|
|
return "Quartz";
|
|
default:
|
|
throw new BindingException (1047, CurrentPlatform);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// We inject thread checks to MonoTouch.UIKit types, unless there is a [ThreadSafe] attribuet on the type.
|
|
// Set on every call to Generate
|
|
//
|
|
bool type_needs_thread_checks;
|
|
|
|
//
|
|
// If set, the members of this type will get zero copy
|
|
//
|
|
internal bool type_wants_zero_copy;
|
|
|
|
//
|
|
// Used by the public binding generator to populate the
|
|
// class with types that do not exist
|
|
//
|
|
public void RegisterMethodName (string method_name)
|
|
{
|
|
send_methods [method_name] = method_name;
|
|
}
|
|
|
|
//
|
|
// Helpers
|
|
//
|
|
string MakeSig (MethodInfo mi, bool stret, bool aligned = false ) { return MakeSig ("objc_msgSend", stret, mi, aligned); }
|
|
string MakeSuperSig (MethodInfo mi, bool stret, bool aligned = false) { return MakeSig ("objc_msgSendSuper", stret, mi, aligned); }
|
|
|
|
public IEnumerable<string> GeneratedFiles {
|
|
get {
|
|
return generated_files;
|
|
}
|
|
}
|
|
|
|
bool IsNativeType (Type pt)
|
|
{
|
|
return (pt == TypeManager.System_Int32 || pt == TypeManager.System_Int64 || pt == TypeManager.System_Byte || pt == TypeManager.System_Int16);
|
|
}
|
|
|
|
public bool IsNSObject (Type type)
|
|
{
|
|
if (type == TypeManager.NSObject)
|
|
return true;
|
|
|
|
if (type.IsSubclassOf (TypeManager.NSObject))
|
|
return true;
|
|
|
|
if (BindThirdPartyLibrary) {
|
|
var bta = ReflectionExtensions.GetBaseTypeAttribute (type, this);
|
|
if (bta?.BaseType != null)
|
|
return IsNSObject (bta.BaseType);
|
|
|
|
return false;
|
|
}
|
|
|
|
return type.IsInterface;
|
|
}
|
|
|
|
public string PrimitiveType (Type t, bool formatted = false)
|
|
{
|
|
if (t == TypeManager.System_Void)
|
|
return "void";
|
|
|
|
if (t.IsEnum) {
|
|
var enumType = t;
|
|
|
|
t = TypeManager.GetUnderlyingEnumType (t);
|
|
|
|
if (IsNativeEnum (enumType, out var _, out var nativeType))
|
|
return nativeType;
|
|
}
|
|
|
|
if (t == TypeManager.System_Int32)
|
|
return "int";
|
|
if (t == TypeManager.System_Int16)
|
|
return "short";
|
|
if (t == TypeManager.System_Byte)
|
|
return "byte";
|
|
if (t == TypeManager.System_Float)
|
|
return "float";
|
|
if (t == TypeManager.System_Boolean)
|
|
return "bool";
|
|
if (t == TypeManager.System_Char)
|
|
return "char";
|
|
if (t == TypeManager.System_nfloat)
|
|
return "nfloat";
|
|
|
|
return formatted ? FormatType (null, t) : t.Name;
|
|
}
|
|
|
|
// Is this a wrapped type of NSObject from the MonoTouch/MonoMac binding world?
|
|
public bool IsWrappedType (Type t)
|
|
{
|
|
if (t.IsInterface)
|
|
return true;
|
|
if (TypeManager.NSObject != null)
|
|
return t.IsSubclassOf (TypeManager.NSObject) || t == TypeManager.NSObject;
|
|
return false;
|
|
}
|
|
|
|
public bool IsArrayOfWrappedType (Type t)
|
|
{
|
|
return t.IsArray && IsWrappedType (t.GetElementType ());
|
|
}
|
|
|
|
|
|
// Is this type something that derives from DictionaryContainerType (or an interface marked up with StrongDictionary)
|
|
public bool IsDictionaryContainerType (Type t)
|
|
{
|
|
return t.IsSubclassOf (TypeManager.DictionaryContainerType) || (t.IsInterface && AttributeManager.HasAttribute<StrongDictionaryAttribute> (t));
|
|
}
|
|
|
|
//
|
|
// Returns the type that we use to marshal the given type as a string
|
|
// for example "UIView" -> "IntPtr"
|
|
string ParameterGetMarshalType (MarshalInfo mai, bool formatted = false)
|
|
{
|
|
if (mai.IsAligned)
|
|
return "IntPtr";
|
|
|
|
if (mai.Type.IsEnum)
|
|
return PrimitiveType (mai.Type, formatted);
|
|
|
|
if (IsWrappedType (mai.Type))
|
|
return mai.Type.IsByRef ? "ref " + NativeHandleType : NativeHandleType;
|
|
|
|
if (mai.Type.Namespace == "System") {
|
|
if (mai.Type.Name == "nint")
|
|
return "IntPtr";
|
|
if (mai.Type.Name == "nuint")
|
|
return "UIntPtr";
|
|
}
|
|
|
|
if (IsNativeType (mai.Type))
|
|
return PrimitiveType (mai.Type, formatted);
|
|
|
|
if (mai.Type == TypeManager.System_String){
|
|
if (mai.PlainString)
|
|
return "string";
|
|
|
|
// We will do NSString
|
|
return NativeHandleType;
|
|
}
|
|
|
|
MarshalType mt;
|
|
if (LookupMarshal (mai.Type, out mt))
|
|
return mt.Encoding;
|
|
|
|
if (mai.Type.IsValueType)
|
|
return PrimitiveType (mai.Type, formatted);
|
|
|
|
// Arrays are returned as NSArrays
|
|
if (mai.Type.IsArray)
|
|
return NativeHandleType;
|
|
|
|
//
|
|
// Pass "out ValueType" directly
|
|
//
|
|
if (mai.Type.IsByRef && mai.Type.GetElementType ().IsValueType){
|
|
Type elementType = mai.Type.GetElementType ();
|
|
|
|
return (mai.IsOut ? "out " : "ref ") + (formatted ? FormatType (null, elementType) : elementType.Name);
|
|
}
|
|
|
|
if (mai.Type.IsSubclassOf (TypeManager.System_Delegate)){
|
|
return NativeHandleType;
|
|
}
|
|
|
|
if (IsDictionaryContainerType(mai.Type)){
|
|
return NativeHandleType;
|
|
}
|
|
|
|
//
|
|
// Edit the table in the "void Go ()" routine
|
|
//
|
|
|
|
if (mai.Type.IsByRef && mai.Type.GetElementType ().IsValueType == false)
|
|
return "ref " + NativeHandleType;
|
|
|
|
if (mai.Type.IsGenericParameter)
|
|
return NativeHandleType;
|
|
|
|
throw new BindingException (1017, true, mai.Type);
|
|
}
|
|
|
|
bool IsProtocolInterface (Type type, bool checkPrefix = true)
|
|
{
|
|
return IsProtocolInterface (type, checkPrefix, out var _);
|
|
}
|
|
|
|
bool IsProtocolInterface (Type type, bool checkPrefix, out Type protocol)
|
|
{
|
|
protocol = null;
|
|
// for subclassing the type (from the binding files) is not yet prefixed by an `I`
|
|
if (checkPrefix && type.Name [0] != 'I')
|
|
return false;
|
|
|
|
protocol = type;
|
|
if (AttributeManager.HasAttribute<ProtocolAttribute> (type))
|
|
return true;
|
|
|
|
protocol = type.Assembly.GetType (type.Namespace + "." + type.Name.Substring (1), false);
|
|
if (protocol == null)
|
|
return false;
|
|
|
|
return AttributeManager.HasAttribute<ProtocolAttribute> (protocol);
|
|
}
|
|
|
|
string FindProtocolInterface (Type type, MemberInfo pi)
|
|
{
|
|
var isArray = type.IsArray;
|
|
var declType = isArray ? type.GetElementType () : type;
|
|
|
|
if (!AttributeManager.HasAttribute<ProtocolAttribute> (declType))
|
|
throw new BindingException (1034, true,
|
|
pi.DeclaringType, pi.Name, declType);
|
|
|
|
return "I" + type.Name;
|
|
}
|
|
|
|
public BindAsAttribute GetBindAsAttribute (ICustomAttributeProvider cu)
|
|
{
|
|
BindAsAttribute rv;
|
|
if (cu != null && (rv = AttributeManager.GetCustomAttribute<BindAsAttribute> (cu)) != null)
|
|
return rv;
|
|
|
|
var minfo = cu as MethodInfo;
|
|
if (minfo?.ReturnParameter != null && (rv = AttributeManager.GetCustomAttribute<BindAsAttribute> (minfo.ReturnParameter)) != null)
|
|
return rv;
|
|
|
|
return null;
|
|
}
|
|
public bool HasBindAsAttribute (ICustomAttributeProvider cu)
|
|
{
|
|
return GetBindAsAttribute (cu) != null;
|
|
}
|
|
|
|
static bool IsSetter (MethodInfo mi) => mi.IsSpecialName && mi.Name.StartsWith ("set_", StringComparison.Ordinal);
|
|
static BindingException GetBindAsException (string box, string retType, string containerType, string container, params ICustomAttributeProvider [] providers)
|
|
{
|
|
Type declaringType = null;
|
|
string memberName = null;
|
|
foreach (var provider in providers) {
|
|
if (provider is MemberInfo member) {
|
|
declaringType = member.DeclaringType;
|
|
memberName = member.Name;
|
|
} else if (provider is ParameterInfo parameter) {
|
|
declaringType = parameter.Member.DeclaringType;
|
|
memberName = parameter.Member.Name;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (declaringType != null && memberName != null)
|
|
memberName = declaringType.FullName + "." + memberName;
|
|
|
|
return new BindingException (1049, true, box, retType, containerType, container, memberName);
|
|
}
|
|
bool IsMemberInsideProtocol (Type type) => IsProtocol (type) || IsModel (type);
|
|
|
|
bool IsSmartEnum (Type type)
|
|
{
|
|
if (!type.IsEnum)
|
|
return false;
|
|
// First check if the smart enum candidate still holds the FieldAtttribute data
|
|
if (type.GetFields ().Any (f => AttributeManager.GetCustomAttribute<FieldAttribute> (f) != null))
|
|
return true;
|
|
// If the above fails it's possible that it comes from another dll (like X.I.dll) so we look for the [Enum]Extensions class existence
|
|
return type.Assembly.GetType (type.FullName + "Extensions") != null;
|
|
}
|
|
|
|
Dictionary<Type,string> nsvalue_create_map;
|
|
Dictionary<Type,string> NSValueCreateMap {
|
|
get {
|
|
if (nsvalue_create_map == null) {
|
|
nsvalue_create_map = new Dictionary<Type, string> ();
|
|
nsvalue_create_map [TypeManager.CGAffineTransform] = "CGAffineTransform";
|
|
nsvalue_create_map [TypeManager.NSRange] = "Range";
|
|
nsvalue_create_map [TypeManager.CGVector] = "CGVector";
|
|
nsvalue_create_map [TypeManager.SCNMatrix4] = "SCNMatrix4";
|
|
nsvalue_create_map [TypeManager.CLLocationCoordinate2D] = "MKCoordinate";
|
|
nsvalue_create_map [TypeManager.SCNVector3] = "Vector";
|
|
nsvalue_create_map [TypeManager.SCNVector4] = "Vector";
|
|
|
|
nsvalue_create_map [TypeManager.CoreGraphics_CGPoint] = "CGPoint";
|
|
nsvalue_create_map [TypeManager.CoreGraphics_CGRect] = "CGRect";
|
|
nsvalue_create_map [TypeManager.CoreGraphics_CGSize] = "CGSize";
|
|
|
|
if (Frameworks.HaveUIKit) {
|
|
nsvalue_create_map [TypeManager.UIEdgeInsets] = "UIEdgeInsets";
|
|
nsvalue_create_map [TypeManager.UIOffset] = "UIOffset";
|
|
nsvalue_create_map [TypeManager.NSDirectionalEdgeInsets] = "DirectionalEdgeInsets";
|
|
}
|
|
|
|
if (TypeManager.MKCoordinateSpan != null)
|
|
nsvalue_create_map [TypeManager.MKCoordinateSpan] = "MKCoordinateSpan";
|
|
|
|
if (Frameworks.HaveCoreMedia) {
|
|
nsvalue_create_map [TypeManager.CMTimeRange] = "CMTimeRange";
|
|
nsvalue_create_map [TypeManager.CMTime] = "CMTime";
|
|
nsvalue_create_map [TypeManager.CMTimeMapping] = "CMTimeMapping";
|
|
}
|
|
|
|
if (Frameworks.HaveCoreAnimation)
|
|
nsvalue_create_map [TypeManager.CATransform3D] = "CATransform3D";
|
|
}
|
|
return nsvalue_create_map;
|
|
}
|
|
}
|
|
|
|
string GetToBindAsWrapper (MethodInfo mi, MemberInformation minfo = null, ParameterInfo pi = null)
|
|
{
|
|
BindAsAttribute attrib = null;
|
|
Type originalType = null;
|
|
string temp = null;
|
|
var declaringType = minfo?.mi?.DeclaringType ?? pi?.Member?.DeclaringType;
|
|
|
|
if (IsMemberInsideProtocol (declaringType))
|
|
throw new BindingException (1050, true, declaringType.Name);
|
|
|
|
if (pi == null) {
|
|
attrib = GetBindAsAttribute (minfo.mi);
|
|
var property = minfo.mi as PropertyInfo;
|
|
var method = minfo.mi as MethodInfo;
|
|
originalType = method?.ReturnType ?? property?.PropertyType;
|
|
} else {
|
|
attrib = GetBindAsAttribute (pi);
|
|
originalType = pi.ParameterType;
|
|
}
|
|
|
|
if (originalType.IsByRef)
|
|
throw new BindingException (1080, true, originalType.Name.Replace ("&", string.Empty));
|
|
|
|
var retType = TypeManager.GetUnderlyingNullableType (attrib.Type) ?? attrib.Type;
|
|
var isNullable = attrib.IsNullable (this);
|
|
var isValueType = retType.IsValueType;
|
|
var isEnum = retType.IsEnum;
|
|
var parameterName = pi != null ? pi.Name.GetSafeParamName () : "value";
|
|
var denullify = isNullable ? ".Value" : string.Empty;
|
|
var nullCheck = isNullable ? $"{parameterName} is null ? null : " : string.Empty;
|
|
|
|
if (isNullable || !isValueType)
|
|
temp = string.Format ("{0} is null ? null : ", parameterName);
|
|
|
|
if (originalType == TypeManager.NSNumber) {
|
|
var enumCast = isEnum ? $"(int)" : string.Empty;
|
|
temp = string.Format ("{3}new NSNumber ({2}{1}{0});", denullify, parameterName, enumCast, nullCheck);
|
|
}
|
|
else if (originalType == TypeManager.NSValue) {
|
|
var typeStr = string.Empty;
|
|
if (!NSValueCreateMap.TryGetValue (retType, out typeStr)) {
|
|
// HACK: These are problematic for X.M due to we do not ship System.Drawing for Full profile
|
|
if (retType.Name == "RectangleF" || retType.Name == "SizeF" || retType.Name == "PointF")
|
|
typeStr = retType.Name;
|
|
else
|
|
throw GetBindAsException ("box", retType.Name, originalType.Name, "container", minfo?.mi, pi);
|
|
}
|
|
temp = string.Format ("{3}NSValue.From{0} ({2}{1});", typeStr, denullify, parameterName, nullCheck);
|
|
} else if (originalType == TypeManager.NSString && IsSmartEnum (retType)) {
|
|
temp = isNullable ? $"{parameterName} is null ? null : " : string.Empty;
|
|
temp += $"{FormatType (retType.DeclaringType, retType)}Extensions.GetConstant ({parameterName}{denullify});";
|
|
} else if (originalType.IsArray && originalType.GetArrayRank () == 1) {
|
|
if (!retType.IsArray) {
|
|
throw new BindingException (1072, true,
|
|
parameterName,mi.DeclaringType.FullName, mi.Name);
|
|
}
|
|
var arrType = originalType.GetElementType ();
|
|
var arrRetType = TypeManager.GetUnderlyingNullableType (retType.GetElementType ()) ?? retType.GetElementType ();
|
|
var valueConverter = string.Empty;
|
|
|
|
if (arrType == TypeManager.NSString && !isNullable) {
|
|
valueConverter = isNullable ? "o == null ? null : " : string.Empty;
|
|
valueConverter += $"{FormatType (retType.DeclaringType, arrRetType)}Extensions.GetConstant ({(isNullable ? "o.Value" : "o")}), {parameterName});";
|
|
} else if (arrType == TypeManager.NSNumber && !isNullable) {
|
|
var cast = arrRetType.IsEnum ? "(int)" : string.Empty;
|
|
valueConverter = $"new NSNumber ({cast}o{denullify}), {parameterName});";
|
|
} else if (arrType == TypeManager.NSValue && !isNullable) {
|
|
var typeStr = string.Empty;
|
|
if (!NSValueCreateMap.TryGetValue (arrRetType, out typeStr)) {
|
|
if (arrRetType.Name == "RectangleF" || arrRetType.Name == "SizeF" || arrRetType.Name == "PointF")
|
|
typeStr = retType.Name;
|
|
else
|
|
throw GetBindAsException ("box", arrRetType.Name, originalType.Name, "array", minfo?.mi, pi);
|
|
}
|
|
valueConverter = $"NSValue.From{typeStr} (o{denullify}), {parameterName});";
|
|
} else
|
|
throw new BindingException (1048, true, isNullable ? arrRetType.Name + "?[]" : retType.Name);
|
|
temp = $"NSArray.FromNSObjects (o => {valueConverter}";
|
|
} else
|
|
throw new BindingException (1048, true, retType.Name);
|
|
|
|
return temp;
|
|
}
|
|
|
|
Dictionary<Type,string> nsnumber_return_map;
|
|
Dictionary<Type,string> NSNumberReturnMap {
|
|
get {
|
|
if (nsnumber_return_map == null) {
|
|
nsnumber_return_map = new Dictionary<Type, string> {
|
|
{ TypeManager.System_Boolean, ".BoolValue" },
|
|
{ TypeManager.System_Byte, ".ByteValue" },
|
|
{ TypeManager.System_Double, ".DoubleValue" },
|
|
{ TypeManager.System_Float, ".FloatValue" },
|
|
{ TypeManager.System_Int16, ".Int16Value" },
|
|
{ TypeManager.System_Int32, ".Int32Value" },
|
|
{ TypeManager.System_Int64, ".Int64Value" },
|
|
{ TypeManager.System_SByte, ".SByteValue" },
|
|
{ TypeManager.System_UInt16, ".UInt16Value" },
|
|
{ TypeManager.System_UInt32, ".UInt32Value" },
|
|
{ TypeManager.System_UInt64, ".UInt64Value" },
|
|
};
|
|
nsnumber_return_map [TypeManager.System_nfloat] = ".NFloatValue";
|
|
nsnumber_return_map [TypeManager.System_nint] = ".NIntValue";
|
|
nsnumber_return_map [TypeManager.System_nuint] = ".NUIntValue";
|
|
}
|
|
return nsnumber_return_map;
|
|
}
|
|
}
|
|
|
|
Dictionary<Type,string> nsvalue_return_map;
|
|
Dictionary<Type,string> NSValueReturnMap {
|
|
get {
|
|
if (nsvalue_return_map == null) {
|
|
nsvalue_return_map = new Dictionary<Type, string> {
|
|
{ TypeManager.CGAffineTransform, ".CGAffineTransformValue" },
|
|
{ TypeManager.NSRange, ".RangeValue" },
|
|
{ TypeManager.CGVector, ".CGVectorValue" },
|
|
{ TypeManager.SCNMatrix4, ".SCNMatrix4Value" },
|
|
{ TypeManager.CLLocationCoordinate2D, ".CoordinateValue" },
|
|
{ TypeManager.SCNVector3, ".Vector3Value" },
|
|
{ TypeManager.SCNVector4, ".Vector4Value" }
|
|
};
|
|
|
|
nsvalue_return_map [TypeManager.CoreGraphics_CGPoint] = ".CGPointValue";
|
|
nsvalue_return_map [TypeManager.CoreGraphics_CGRect] = ".CGRectValue";
|
|
nsvalue_return_map [TypeManager.CoreGraphics_CGSize] = ".CGSizeValue";
|
|
|
|
if (Frameworks.HaveUIKit) {
|
|
nsvalue_return_map [TypeManager.UIEdgeInsets] = ".UIEdgeInsetsValue";
|
|
nsvalue_return_map [TypeManager.UIOffset] = ".UIOffsetValue";
|
|
nsvalue_return_map [TypeManager.NSDirectionalEdgeInsets] = ".DirectionalEdgeInsetsValue";
|
|
}
|
|
|
|
if (TypeManager.MKCoordinateSpan != null)
|
|
nsvalue_return_map [TypeManager.MKCoordinateSpan] = ".CoordinateSpanValue";
|
|
|
|
if (Frameworks.HaveCoreMedia) {
|
|
nsvalue_return_map [TypeManager.CMTimeRange] = ".CMTimeRangeValue";
|
|
nsvalue_return_map [TypeManager.CMTime] = ".CMTimeValue";
|
|
nsvalue_return_map [TypeManager.CMTimeMapping] = ".CMTimeMappingValue";
|
|
}
|
|
|
|
if (Frameworks.HaveCoreAnimation)
|
|
nsvalue_return_map [TypeManager.CATransform3D] = ".CATransform3DValue";
|
|
}
|
|
return nsvalue_return_map;
|
|
}
|
|
}
|
|
|
|
string GetFromBindAsWrapper (MemberInformation minfo, out string suffix)
|
|
{
|
|
var declaringType = minfo.mi.DeclaringType;
|
|
if (IsMemberInsideProtocol (declaringType))
|
|
throw new BindingException (1050, true, declaringType.Name);
|
|
|
|
suffix = string.Empty;
|
|
|
|
var attrib = GetBindAsAttribute (minfo.mi);
|
|
var nullableRetType = TypeManager.GetUnderlyingNullableType (attrib.Type);
|
|
var isNullable = nullableRetType != null;
|
|
var retType = isNullable ? nullableRetType : attrib.Type;
|
|
var isValueType = retType.IsValueType;
|
|
var append = string.Empty;
|
|
var property = minfo.mi as PropertyInfo;
|
|
var method = minfo.mi as MethodInfo;
|
|
var originalReturnType = method?.ReturnType ?? property?.PropertyType;
|
|
|
|
if (originalReturnType == TypeManager.NSNumber) {
|
|
if (!NSNumberReturnMap.TryGetValue (retType, out append)) {
|
|
if (retType.IsEnum) {
|
|
var enumType = TypeManager.GetUnderlyingEnumType (retType);
|
|
if (!NSNumberReturnMap.TryGetValue (enumType, out append))
|
|
throw GetBindAsException ("unbox", retType.Name, originalReturnType.Name, "container", minfo.mi);
|
|
}
|
|
else
|
|
throw GetBindAsException ("unbox", retType.Name, originalReturnType.Name, "container", minfo.mi);
|
|
}
|
|
if (isNullable)
|
|
append = $"?{append}";
|
|
} else if (originalReturnType == TypeManager.NSValue) {
|
|
if (!NSValueReturnMap.TryGetValue (retType, out append)) {
|
|
// HACK: These are problematic for X.M due to we do not ship System.Drawing for Full profile
|
|
if (retType.Name == "RectangleF" || retType.Name == "SizeF" || retType.Name == "PointF")
|
|
append = $".{retType.Name}Value";
|
|
else
|
|
throw GetBindAsException ("unbox", retType.Name, originalReturnType.Name, "container", minfo.mi);
|
|
}
|
|
if (isNullable)
|
|
append = $"?{append}";
|
|
} else if (originalReturnType == TypeManager.NSString && IsSmartEnum (retType)) {
|
|
append = $"{FormatType (retType.DeclaringType, retType)}Extensions.GetValue (";
|
|
suffix = ")";
|
|
} else if (originalReturnType.IsArray && originalReturnType.GetArrayRank () == 1) {
|
|
var arrType = originalReturnType.GetElementType ();
|
|
var nullableElementType = TypeManager.GetUnderlyingNullableType (retType.GetElementType ());
|
|
var arrIsNullable = nullableElementType != null;
|
|
var arrRetType = arrIsNullable ? nullableElementType : retType.GetElementType ();
|
|
var valueFetcher = string.Empty;
|
|
if (arrType == TypeManager.NSString && !arrIsNullable)
|
|
append = $"ptr => {{\n\tusing (var str = Runtime.GetNSObject<NSString> (ptr)!) {{\n\t\treturn {FormatType (arrRetType.DeclaringType, arrRetType)}Extensions.GetValue (str);\n\t}}\n}}";
|
|
else if (arrType == TypeManager.NSNumber && !arrIsNullable) {
|
|
if (NSNumberReturnMap.TryGetValue (arrRetType, out valueFetcher) || arrRetType.IsEnum) {
|
|
var getterStr = string.Format ("{0}{1}", arrIsNullable ? "?" : string.Empty, arrRetType.IsEnum ? ".Int32Value" : valueFetcher);
|
|
append = string.Format ("ptr => {{\n\tusing (var num = Runtime.GetNSObject<NSNumber> (ptr)!) {{\n\t\treturn ({1}) num{0};\n\t}}\n}}", getterStr, FormatType (arrRetType.DeclaringType, arrRetType));
|
|
}
|
|
else
|
|
throw GetBindAsException ("unbox", retType.Name, arrType.Name, "array", minfo.mi);
|
|
} else if (arrType == TypeManager.NSValue && !arrIsNullable) {
|
|
if (arrRetType.Name == "RectangleF" || arrRetType.Name == "SizeF" || arrRetType.Name == "PointF")
|
|
valueFetcher = $"{(arrIsNullable ? "?" : string.Empty)}.{arrRetType.Name}Value";
|
|
else if (!NSValueReturnMap.TryGetValue (arrRetType, out valueFetcher))
|
|
throw GetBindAsException ("unbox", retType.Name, arrType.Name, "array", minfo.mi);
|
|
|
|
append = string.Format ("ptr => {{\n\tusing (var val = Runtime.GetNSObject<NSValue> (ptr)!) {{\n\t\treturn val{0};\n\t}}\n}}", valueFetcher);
|
|
} else
|
|
throw new BindingException (1048, true, arrIsNullable ? arrRetType.Name + "?[]" : retType.Name);
|
|
} else
|
|
throw new BindingException (1048, true, retType.Name);
|
|
return append;
|
|
}
|
|
|
|
public bool HasForcedAttribute (ICustomAttributeProvider cu, out string owns)
|
|
{
|
|
var att = AttributeManager.GetCustomAttribute<ForcedTypeAttribute> (cu);
|
|
|
|
if (att == null) {
|
|
var mi = cu as MethodInfo;
|
|
if (mi != null)
|
|
att = AttributeManager.GetCustomAttribute<ForcedTypeAttribute> (mi.ReturnParameter);
|
|
}
|
|
|
|
if (att == null) {
|
|
owns = "false";
|
|
return false;
|
|
}
|
|
|
|
owns = att.Owns ? "true" : "false";
|
|
return true;
|
|
}
|
|
|
|
public string MakeTrampolineName (Type t)
|
|
{
|
|
var trampoline_name = t.Name.Replace ("`", "Arity");
|
|
if (t.IsGenericType) {
|
|
var gdef = t.GetGenericTypeDefinition ();
|
|
|
|
if (!trampolines_generic_versions.ContainsKey (gdef))
|
|
trampolines_generic_versions.Add (gdef, 0);
|
|
|
|
trampoline_name = trampoline_name + "V" + trampolines_generic_versions [gdef]++;
|
|
}
|
|
return trampoline_name;
|
|
}
|
|
|
|
//
|
|
// MakeTrampoline: processes a delegate type and registers a TrampolineInfo with all the information
|
|
// necessary to create trampolines that allow Objective-C blocks to call C# code, and C# code to call
|
|
// Objective-C blocks.
|
|
//
|
|
// @t: A delegate type
|
|
//
|
|
// This probably should use MarshalInfo to find the correct way of turning
|
|
// the native types into managed types instead of hardcoding the limited
|
|
// values we know about here
|
|
//
|
|
public TrampolineInfo MakeTrampoline (Type t)
|
|
{
|
|
if (trampolines.ContainsKey (t)){
|
|
return trampolines [t];
|
|
}
|
|
|
|
var mi = t.GetMethod ("Invoke");
|
|
var pars = new StringBuilder ();
|
|
var convert = new StringBuilder ();
|
|
var invoke = new StringBuilder ();
|
|
var clear = new StringBuilder ();
|
|
var postConvert = new StringBuilder ();
|
|
string returntype;
|
|
var returnformat = "return {0};";
|
|
|
|
if (mi.ReturnType.IsArray && IsWrappedType (mi.ReturnType.GetElementType())) {
|
|
returntype = NativeHandleType;
|
|
returnformat = "return NSArray.FromNSObjects({0}).Handle;";
|
|
}
|
|
else if (IsWrappedType (mi.ReturnType)) {
|
|
returntype = Generator.NativeHandleType;
|
|
returnformat = "return {0}.GetHandle ();";
|
|
} else if (mi.ReturnType == TypeManager.System_String) {
|
|
returntype = Generator.NativeHandleType;
|
|
returnformat = "return NSString.CreateNative ({0}, true);";
|
|
} else if (GetNativeEnumToNativeExpression (mi.ReturnType, out var preExpression, out var postExpression, out var nativeType)) {
|
|
returntype = nativeType;
|
|
returnformat = "return " + preExpression+ "{0}" + postExpression + ";";
|
|
} else if (TypeManager.INativeObject.IsAssignableFrom (mi.ReturnType)) {
|
|
returntype = Generator.NativeHandleType;
|
|
returnformat = "return {0}.GetHandle ();";
|
|
} else {
|
|
returntype = FormatType (mi.DeclaringType, mi.ReturnType);
|
|
}
|
|
|
|
pars.Append ($"IntPtr block");
|
|
var parameters = mi.GetParameters ();
|
|
foreach (var pi in parameters){
|
|
string isForcedOwns;
|
|
var isForced = HasForcedAttribute (pi, out isForcedOwns);
|
|
|
|
pars.Append (", ");
|
|
if (pi != parameters [0])
|
|
invoke.Append (", ");
|
|
|
|
var safe_name = pi.Name.GetSafeParamName ();
|
|
|
|
if (IsWrappedType (pi.ParameterType)) {
|
|
pars.AppendFormat ("{1} {0}", safe_name, NativeHandleType);
|
|
if (IsProtocolInterface (pi.ParameterType)) {
|
|
invoke.AppendFormat (" Runtime.GetINativeObject<{1}> ({0}, false)!", safe_name, pi.ParameterType);
|
|
} else if (isForced) {
|
|
invoke.AppendFormat (" Runtime.GetINativeObject<{1}> ({0}, true, {2})!", safe_name, RenderType (pi.ParameterType), isForcedOwns);
|
|
} else {
|
|
invoke.AppendFormat (" Runtime.GetNSObject<{1}> ({0})!", safe_name, RenderType (pi.ParameterType));
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (Frameworks.HaveCoreMedia) {
|
|
// special case (false) so it needs to be before the _real_ INativeObject check
|
|
if (pi.ParameterType == TypeManager.CMSampleBuffer) {
|
|
pars.AppendFormat ("{1} {0}", safe_name, NativeHandleType);
|
|
if (BindThirdPartyLibrary)
|
|
invoke.AppendFormat ("{0} == IntPtr.Zero ? null! : Runtime.GetINativeObject<CMSampleBuffer> ({0}, false)!", safe_name);
|
|
else
|
|
invoke.AppendFormat ("{0} == IntPtr.Zero ? null! : new CMSampleBuffer ({0}, false)", safe_name);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (Frameworks.HaveAudioToolbox) {
|
|
if (pi.ParameterType == TypeManager.AudioBuffers){
|
|
pars.AppendFormat ("{1} {0}", safe_name, NativeHandleType);
|
|
invoke.AppendFormat ("new global::AudioToolbox.AudioBuffers ({0})", safe_name);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (TypeManager.INativeObject.IsAssignableFrom (pi.ParameterType)) {
|
|
pars.AppendFormat ("{1} {0}", safe_name, NativeHandleType);
|
|
invoke.AppendFormat ("new {0} ({1}, false)", pi.ParameterType, safe_name);
|
|
continue;
|
|
}
|
|
|
|
if (pi.ParameterType.IsByRef){
|
|
var nt = pi.ParameterType.GetElementType ();
|
|
if (pi.IsOut){
|
|
clear.AppendFormat ("{0} = {1};", safe_name, nt.IsValueType ? "default (" + FormatType (null, nt) + ")" : "null");
|
|
}
|
|
if (nt.IsValueType){
|
|
string marshal = string.Empty;
|
|
if (nt == TypeManager.System_Boolean)
|
|
marshal = "[System.Runtime.InteropServices.MarshalAs (System.Runtime.InteropServices.UnmanagedType.I1)] ";
|
|
string fnt;
|
|
string invoke_name;
|
|
string arg_byref = pi.IsOut ? "out " : "ref ";
|
|
var nullable = TypeManager.GetUnderlyingNullableType (nt);
|
|
if (nullable != null) {
|
|
fnt = "IntPtr";
|
|
invoke_name = $"__xamarin_nullified__{pi.Position}";
|
|
arg_byref = String.Empty;
|
|
convert.AppendLine ($"{nullable.Name}? {invoke_name} = null;");
|
|
convert.AppendLine ("if (value != IntPtr.Zero)");
|
|
convert.Append ($"\t{invoke_name} = * ({nullable.Name} *) value;");
|
|
} else {
|
|
fnt = FormatType (null, nt);
|
|
invoke_name = safe_name;
|
|
}
|
|
pars.AppendFormat ("{3}{0}{1} {2}", arg_byref, fnt, safe_name, marshal);
|
|
invoke.AppendFormat ("{0} {1}", pi.IsOut ? "out" : "ref", invoke_name);
|
|
continue;
|
|
} else if (pi.ParameterType.IsByRef) {
|
|
var refname = $"__xamarin_pref{pi.Position}";
|
|
convert.Append ($"var {refname} = Runtime.GetINativeObject<{RenderType (nt)}> ({safe_name}, false)!;");
|
|
pars.Append ($"ref {NativeHandleType} {safe_name}");
|
|
postConvert.Append ($"{safe_name} = {refname}.GetHandle ();");
|
|
if (pi.IsOut)
|
|
invoke.Append ("out ");
|
|
else
|
|
invoke.Append ("ref ");
|
|
invoke.Append (refname);
|
|
continue;
|
|
}
|
|
} else if (GetNativeEnumToManagedExpression (pi.ParameterType, out var preExpression, out var postExpression, out var nativeType)) {
|
|
pars.AppendFormat ("{0} {1}", nativeType, safe_name);
|
|
invoke.Append (preExpression).Append (safe_name).Append (postExpression);
|
|
continue;
|
|
} else if (pi.ParameterType.IsValueType){
|
|
pars.AppendFormat ("{0} {1}", FormatType (null, pi.ParameterType), safe_name);
|
|
invoke.AppendFormat ("{0}", safe_name);
|
|
continue;
|
|
}
|
|
|
|
if (pi.ParameterType == TypeManager.System_String_Array){
|
|
pars.AppendFormat ("{1} {0}", safe_name, NativeHandleType);
|
|
invoke.AppendFormat ("CFArray.StringArrayFromHandle ({0})!", safe_name);
|
|
continue;
|
|
}
|
|
if (pi.ParameterType == TypeManager.System_String) {
|
|
pars.AppendFormat ("{1} {0}", safe_name, NativeHandleType);
|
|
invoke.AppendFormat ("CFString.FromHandle ({0})!", safe_name);
|
|
continue;
|
|
}
|
|
|
|
if (pi.ParameterType.IsArray){
|
|
Type et = pi.ParameterType.GetElementType ();
|
|
if (IsWrappedType (et)) {
|
|
pars.AppendFormat ("{1} {0}", safe_name, NativeHandleType);
|
|
invoke.AppendFormat ("CFArray.ArrayFromHandle<{0}> ({1})!", FormatType (null, et), safe_name);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (pi.ParameterType.IsSubclassOf (TypeManager.System_Delegate)){
|
|
if (!delegate_types.ContainsKey (pi.ParameterType.Name)){
|
|
delegate_types [pi.ParameterType.FullName] = pi.ParameterType.GetMethod ("Invoke");
|
|
}
|
|
if (AttributeManager.HasAttribute<BlockCallbackAttribute> (pi)) {
|
|
pars.AppendFormat ("{1} {0}", safe_name, NativeHandleType);
|
|
invoke.AppendFormat ("NID{0}.Create ({1})!", MakeTrampolineName (pi.ParameterType), safe_name);
|
|
// The trampoline will eventually be generated in the final loop
|
|
} else {
|
|
if (!AttributeManager.HasAttribute<CCallbackAttribute> (pi)) {
|
|
if (t.FullName.StartsWith ("System.Action`", StringComparison.Ordinal) || t.FullName.StartsWith ("System.Func`", StringComparison.Ordinal)) {
|
|
ErrorHelper.Warning (1116, safe_name, t.FullName);
|
|
} else {
|
|
ErrorHelper.Warning (1115, safe_name, t.FullName);
|
|
}
|
|
}
|
|
pars.AppendFormat ("{1} {0}", safe_name, NativeHandleType);
|
|
invoke.AppendFormat ("({0}) Marshal.GetDelegateForFunctionPointer ({1}, typeof ({0}))", pi.ParameterType, safe_name);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
throw new BindingException (1001, true, pi.ParameterType.FullName);
|
|
}
|
|
|
|
var rt = mi.ReturnType;
|
|
var rts = IsNativeEnum (rt) ? "var" : rt.ToString ();
|
|
var trampoline_name = MakeTrampolineName (t);
|
|
var ti = new TrampolineInfo (userDelegate: FormatType (null, t),
|
|
delegateName: "D" + trampoline_name,
|
|
trampolineName: "T" + trampoline_name,
|
|
pars: pars.ToString (),
|
|
convert: convert.ToString (),
|
|
invoke: invoke.ToString (),
|
|
returnType: returntype,
|
|
delegateReturnType: rts,
|
|
returnFormat: returnformat,
|
|
clear: clear.ToString (),
|
|
postConvert: postConvert.ToString (),
|
|
type: t);
|
|
|
|
|
|
ti.UserDelegateTypeAttribute = FormatType (null, t);
|
|
trampolines [t] = ti;
|
|
|
|
return ti;
|
|
}
|
|
|
|
//
|
|
// Returns the actual way in which the type t must be marshalled
|
|
// for example "UIView foo" is generated as "foo.Handle"
|
|
//
|
|
public string MarshalParameter (MethodInfo mi, ParameterInfo pi, bool null_allowed_override, PropertyInfo propInfo = null, bool castEnum = true)
|
|
{
|
|
if (pi.ParameterType.IsByRef && pi.ParameterType.GetElementType ().IsValueType == false){
|
|
return "ref " + pi.Name + "Value";
|
|
}
|
|
|
|
if (HasBindAsAttribute (pi))
|
|
return string.Format ("nsb_{0}.GetHandle ()", pi.Name);
|
|
if (propInfo != null && HasBindAsAttribute (propInfo))
|
|
return string.Format ("nsb_{0}.GetHandle ()", propInfo.Name);
|
|
|
|
var safe_name = pi.Name.GetSafeParamName ();
|
|
|
|
if (IsWrappedType (pi.ParameterType))
|
|
return safe_name + "__handle__";
|
|
|
|
if (GetNativeEnumToNativeExpression (pi.ParameterType, out var preExpression, out var postExpression, out var nativeType))
|
|
return preExpression + safe_name + postExpression;
|
|
|
|
if (castEnum && pi.ParameterType.IsEnum)
|
|
return "(" + PrimitiveType (pi.ParameterType) + ")" + safe_name;
|
|
|
|
if (castEnum && pi.ParameterType.Namespace == "System") {
|
|
if (pi.ParameterType.Name == "nint")
|
|
return "(IntPtr) " + safe_name;
|
|
else if (pi.ParameterType.Name == "nuint")
|
|
return "(UIntPtr) " + safe_name;
|
|
}
|
|
|
|
if (IsNativeType (pi.ParameterType))
|
|
return safe_name;
|
|
|
|
if (pi.ParameterType == TypeManager.System_String){
|
|
var mai = new MarshalInfo (this, mi, pi);
|
|
if (mai.PlainString)
|
|
return safe_name;
|
|
else {
|
|
bool allow_null = null_allowed_override || AttributeManager.HasAttribute<NullAllowedAttribute> (pi);
|
|
|
|
if (mai.ZeroCopyStringMarshal){
|
|
if (allow_null)
|
|
return String.Format ("{0} is null ? IntPtr.Zero : (IntPtr)(&_s{0})", pi.Name);
|
|
else
|
|
return String.Format ("(IntPtr)(&_s{0})", pi.Name);
|
|
} else {
|
|
return "ns" + pi.Name;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pi.ParameterType.IsValueType)
|
|
return safe_name;
|
|
|
|
MarshalType mt;
|
|
if (LookupMarshal (pi.ParameterType, out mt)){
|
|
if (null_allowed_override || AttributeManager.HasAttribute<NullAllowedAttribute> (pi))
|
|
return safe_name + "__handle__";
|
|
return String.Format (mt.ParameterMarshal, safe_name);
|
|
}
|
|
|
|
if (pi.ParameterType.IsArray){
|
|
//Type etype = pi.ParameterType.GetElementType ();
|
|
|
|
if (null_allowed_override || AttributeManager.HasAttribute<NullAllowedAttribute> (pi))
|
|
return String.Format ("nsa_{0}.GetHandle ()", pi.Name);
|
|
return "nsa_" + pi.Name + ".Handle";
|
|
}
|
|
|
|
//
|
|
// Handle (out ValueType foo)
|
|
//
|
|
if (pi.ParameterType.IsByRef) {
|
|
var et = pi.ParameterType.GetElementType ();
|
|
var nullable = TypeManager.GetUnderlyingNullableType (et);
|
|
if (nullable != null) {
|
|
return "converted";
|
|
} else if (et.IsValueType)
|
|
return (TypeManager.IsOutParameter (pi) ? "out " : "ref ") + safe_name;
|
|
}
|
|
|
|
if (pi.ParameterType.IsSubclassOf (TypeManager.System_Delegate)){
|
|
return String.Format ("(IntPtr) block_ptr_{0}", pi.Name);
|
|
}
|
|
|
|
if (IsDictionaryContainerType(pi.ParameterType)){
|
|
if (null_allowed_override || AttributeManager.HasAttribute<NullAllowedAttribute> (pi))
|
|
return String.Format ("{0} is null ? NativeHandle.Zero : {0}.Dictionary.Handle", safe_name);
|
|
return safe_name + ".Dictionary.Handle";
|
|
}
|
|
|
|
if (pi.ParameterType.IsGenericParameter) {
|
|
if (null_allowed_override || AttributeManager.HasAttribute<NullAllowedAttribute> (pi))
|
|
return string.Format ("{0}.GetHandle ()", safe_name);
|
|
return safe_name + ".Handle";
|
|
}
|
|
|
|
// This means you need to add a new MarshalType in the method "Go"
|
|
throw new BindingException (1002, true, pi.ParameterType.FullName, mi.DeclaringType.FullName, mi.Name.GetSafeParamName ());
|
|
}
|
|
|
|
public bool ParameterNeedsNullCheck (ParameterInfo pi, MethodInfo mi, PropertyInfo propInfo = null)
|
|
{
|
|
if (pi.ParameterType.IsByRef)
|
|
return false;
|
|
|
|
if (AttributeManager.HasAttribute<NullAllowedAttribute> (pi))
|
|
return false;
|
|
|
|
if (IsSetter (mi)) {
|
|
if (AttributeManager.HasAttribute<NullAllowedAttribute> (mi)) {
|
|
return false;
|
|
}
|
|
if ((propInfo != null) && AttributeManager.HasAttribute<NullAllowedAttribute> (propInfo)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
var bindAsAtt = GetBindAsAttribute (pi) ?? GetBindAsAttribute (propInfo);
|
|
if (bindAsAtt != null)
|
|
return bindAsAtt.IsNullable (this) || !bindAsAtt.IsValueType (this);
|
|
|
|
if (IsWrappedType (pi.ParameterType))
|
|
return true;
|
|
|
|
return !pi.ParameterType.IsValueType;
|
|
}
|
|
|
|
public BindAttribute GetBindAttribute (MethodInfo mi)
|
|
{
|
|
return AttributeManager.GetCustomAttribute<BindAttribute> (mi);
|
|
}
|
|
|
|
public bool HasAttribute<T> (ICustomAttributeProvider i, Attribute [] attributes) where T: Attribute
|
|
{
|
|
if (attributes == null)
|
|
return AttributeManager.HasAttribute<T> (i);
|
|
else
|
|
foreach (var a in attributes)
|
|
if (a.GetType () == typeof (T))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
public bool ShouldMarshalNativeExceptions (MethodInfo mi)
|
|
{
|
|
// [MarshalNativeExceptions] should work on a property and inside the get / set
|
|
// If we have it directly on our method, good enough
|
|
if (AttributeManager.HasAttribute<MarshalNativeExceptionsAttribute> (mi))
|
|
return true;
|
|
|
|
// Else look up to see if we are part of a property and look for the attribute there
|
|
PropertyInfo owningProperty = mi.DeclaringType.GetProperties ()
|
|
.FirstOrDefault(prop => prop.GetSetMethod() == mi ||
|
|
prop.GetGetMethod() == mi);
|
|
if (owningProperty != null && AttributeManager.HasAttribute<MarshalNativeExceptionsAttribute> (owningProperty))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
public bool IsTarget (ParameterInfo pi)
|
|
{
|
|
var is_target = AttributeManager.HasAttribute<TargetAttribute> (pi);
|
|
if (is_target) {
|
|
throw new BindingException (1031, true,
|
|
pi.Member.DeclaringType.FullName, pi.Member.Name.GetSafeParamName ());
|
|
}
|
|
return is_target;
|
|
}
|
|
|
|
//
|
|
// Makes the method name for a objcSend call
|
|
//
|
|
string MakeSig (string send, bool stret, MethodInfo mi, bool aligned)
|
|
{
|
|
var sb = new StringBuilder ();
|
|
var shouldMarshalNativeExceptions = ShouldMarshalNativeExceptions (mi);
|
|
var marshalDirective = AttributeManager.GetCustomAttribute<MarshalDirectiveAttribute> (mi);
|
|
|
|
if (!string.IsNullOrEmpty (marshalDirective?.NativePrefix))
|
|
sb.Append (marshalDirective.NativePrefix);
|
|
else if (shouldMarshalNativeExceptions)
|
|
sb.Append ("xamarin_");
|
|
|
|
try {
|
|
sb.Append (ParameterGetMarshalType (new MarshalInfo (this, mi) { IsAligned = aligned } ));
|
|
} catch (BindingException ex) {
|
|
throw new BindingException (1078, ex.Error, ex, ex.Message, mi.Name);
|
|
}
|
|
|
|
sb.Append ("_");
|
|
sb.Append (send);
|
|
if (stret)
|
|
sb.Append ("_stret");
|
|
|
|
foreach (var pi in mi.GetParameters ()){
|
|
if (IsTarget (pi))
|
|
continue;
|
|
sb.Append ("_");
|
|
try {
|
|
sb.Append (ParameterGetMarshalType (new MarshalInfo (this, mi, pi)).Replace (' ', '_'));
|
|
} catch (BindingException ex) {
|
|
throw new BindingException (1079, ex.Error, ex, ex.Message, pi.Name.GetSafeParamName (), mi.DeclaringType, mi.Name);
|
|
}
|
|
}
|
|
|
|
if (shouldMarshalNativeExceptions)
|
|
sb.Append ("_exception");
|
|
|
|
if (!string.IsNullOrEmpty (marshalDirective?.NativeSuffix))
|
|
sb.Append (marshalDirective.NativeSuffix);
|
|
|
|
return sb.ToString ();
|
|
}
|
|
|
|
void RegisterMethod (bool need_stret, MethodInfo mi, string method_name, bool aligned)
|
|
{
|
|
if (send_methods.ContainsKey (method_name))
|
|
return;
|
|
send_methods [method_name] = method_name;
|
|
|
|
var b = new StringBuilder ();
|
|
int n = 0;
|
|
|
|
foreach (var pi in mi.GetParameters ()){
|
|
if (IsTarget (pi))
|
|
continue;
|
|
|
|
b.Append (", ");
|
|
|
|
try {
|
|
var parameterType = ParameterGetMarshalType (new MarshalInfo (this, mi, pi), true);
|
|
if (parameterType == "bool" || parameterType == "out bool" || parameterType == "ref bool")
|
|
b.Append ("[MarshalAs (UnmanagedType.I1)] ");
|
|
else if (parameterType == "char" || parameterType == "out char" || parameterType == "ref char")
|
|
b.Append ("[MarshalAs (UnmanagedType.U2)] ");
|
|
b.Append (parameterType);
|
|
} catch (BindingException ex) {
|
|
throw new BindingException (1079, ex.Error, ex, ex.Message, pi.Name.GetSafeParamName (), mi.DeclaringType, mi.Name);
|
|
}
|
|
b.Append (" ");
|
|
b.Append ("arg" + (++n));
|
|
}
|
|
|
|
if (ShouldMarshalNativeExceptions (mi))
|
|
b.Append (", out IntPtr exception_gchandle");
|
|
|
|
string entry_point;
|
|
if (method_name.IndexOf ("objc_msgSendSuper", StringComparison.Ordinal) != -1) {
|
|
entry_point = need_stret ? "objc_msgSendSuper_stret" : "objc_msgSendSuper";
|
|
} else
|
|
entry_point = need_stret ? "objc_msgSend_stret" : "objc_msgSend";
|
|
|
|
var marshalDirective = AttributeManager.GetCustomAttribute<MarshalDirectiveAttribute> (mi);
|
|
if (marshalDirective != null && marshalDirective.Library != null) {
|
|
print (m, "\t\t[DllImport (\"{0}\", EntryPoint=\"{1}\")]", marshalDirective.Library, method_name);
|
|
} else if (method_name.StartsWith ("xamarin_", StringComparison.Ordinal)) {
|
|
print (m, "\t\t[DllImport (\"__Internal\", EntryPoint=\"{0}\")]", method_name);
|
|
} else {
|
|
print (m, "\t\t[DllImport (LIBOBJC_DYLIB, EntryPoint=\"{0}\")]", entry_point);
|
|
}
|
|
|
|
var returnType = need_stret ? "void" : ParameterGetMarshalType (new MarshalInfo (this, mi), true);
|
|
if (returnType == "bool")
|
|
print (m, "\t\t[return: MarshalAs (UnmanagedType.I1)]");
|
|
else if (returnType == "char")
|
|
print (m, "\t\t[return: MarshalAs (UnmanagedType.U2)]");
|
|
|
|
print (m, "\t\tpublic extern static {0} {1} ({3}IntPtr receiver, IntPtr selector{2});",
|
|
returnType, method_name, b.ToString (),
|
|
need_stret ? (aligned ? "IntPtr" : "out " + FormatTypeUsedIn ("ObjCRuntime", mi.ReturnType)) + " retval, " : "");
|
|
}
|
|
|
|
bool IsNativeEnum (Type type)
|
|
{
|
|
return type.IsEnum && AttributeManager.HasAttribute<NativeAttribute> (type);
|
|
}
|
|
|
|
// Returns two strings that are used to convert from a native (nint/nuint) value to a managed ([Native]) enum value
|
|
// nativeType: nint/nuint
|
|
bool GetNativeEnumToManagedExpression (Type enumType, out string preExpression, out string postExpression, out string nativeType, StringBuilder postproc = null)
|
|
{
|
|
preExpression = null;
|
|
postExpression = null;
|
|
nativeType = null;
|
|
|
|
if (!enumType.IsEnum)
|
|
return false;
|
|
|
|
var attrib = AttributeManager.GetCustomAttribute<NativeAttribute> (enumType);
|
|
if (attrib is null)
|
|
return false;
|
|
|
|
var renderedEnumType = RenderType (enumType);
|
|
var underlyingEnumType = TypeManager.GetUnderlyingEnumType (enumType);
|
|
var underlyingTypeName = RenderType (underlyingEnumType);
|
|
string itype;
|
|
string intermediateType;
|
|
object maxValue;
|
|
Func<FieldInfo, bool> isMaxDefinedFunc;
|
|
Func<FieldInfo, bool> isMinDefinedFunc = null;
|
|
if (TypeManager.System_Int64 == underlyingEnumType) {
|
|
nativeType = "IntPtr";
|
|
intermediateType = "nint";
|
|
itype = "int";
|
|
maxValue = long.MaxValue;
|
|
isMaxDefinedFunc = (v) => (long) v.GetRawConstantValue () == long.MaxValue;
|
|
isMinDefinedFunc = (v) => (long) v.GetRawConstantValue () == long.MinValue;
|
|
} else if (TypeManager.System_UInt64 == underlyingEnumType) {
|
|
nativeType = "UIntPtr";
|
|
intermediateType = "nuint";
|
|
itype = "uint";
|
|
maxValue = ulong.MaxValue;
|
|
isMaxDefinedFunc = (v) => (ulong) v.GetRawConstantValue () == ulong.MaxValue;
|
|
} else {
|
|
throw new BindingException (1029, enumType);
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty (attrib.ConvertToManaged)) {
|
|
preExpression = attrib.ConvertToManaged + " ((" + intermediateType + ") ";
|
|
postExpression = ")";
|
|
} else {
|
|
preExpression = "(" + renderedEnumType + ") (" + RenderType (underlyingEnumType) + ") ";
|
|
postExpression = string.Empty;
|
|
}
|
|
|
|
// Check if we got UInt32.MaxValue, which should probably be UInt64.MaxValue (if the enum
|
|
// in question actually has that value at least). Same goes for Int32.MinValue/Int64.MinValue.
|
|
// var isDefined = enumType.IsEnumDefined (maxValue);
|
|
var definedMaxField = enumType.GetFields ().Where (v => v.IsLiteral).FirstOrDefault (isMaxDefinedFunc);
|
|
if (definedMaxField is not null && postproc is not null) {
|
|
postproc.AppendLine ("#if ARCH_32");
|
|
postproc.AppendFormat ("if (({0}) ret == ({0}) {1}.MaxValue)\n", underlyingTypeName, itype);
|
|
postproc.AppendFormat ("\tret = {0}.{1}; // = {2}.MaxValue\n", renderedEnumType, definedMaxField.Name, underlyingTypeName);
|
|
if (underlyingEnumType == TypeManager.System_Int64) {
|
|
var definedMinField = enumType.GetFields ().Where (v => v.IsLiteral).FirstOrDefault (isMinDefinedFunc);
|
|
if (definedMinField != null) {
|
|
postproc.AppendFormat ("else if (({0}) ret == ({0}) {1}.MinValue)\n", underlyingTypeName, itype);
|
|
postproc.AppendFormat ("\tret = {0}.{1}; // = {2}.MinValue\n", renderedEnumType, definedMinField.Name, underlyingTypeName);
|
|
}
|
|
}
|
|
postproc.AppendLine ("#endif");
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Returns two strings that are used to convert from a managed enum value to a native nint/nuint.
|
|
// nativeType: nint/nuint
|
|
bool GetNativeEnumToNativeExpression (Type enumType, out string preExpression, out string postExpression, out string nativeType)
|
|
{
|
|
preExpression = null;
|
|
postExpression = null;
|
|
nativeType = null;
|
|
|
|
if (!enumType.IsEnum)
|
|
return false;
|
|
|
|
var attrib = AttributeManager.GetCustomAttribute<NativeAttribute> (enumType);
|
|
if (attrib is null)
|
|
return false;
|
|
|
|
var underlyingEnumType = TypeManager.GetUnderlyingEnumType (enumType);
|
|
if (TypeManager.System_Int64 == underlyingEnumType) {
|
|
nativeType = "IntPtr";
|
|
} else if (TypeManager.System_UInt64 == underlyingEnumType) {
|
|
nativeType = "UIntPtr";
|
|
} else {
|
|
throw new BindingException (1029, enumType);
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty (attrib.ConvertToNative)) {
|
|
preExpression = "(" + nativeType + ") " + attrib.ConvertToNative + " (";
|
|
postExpression = ")";
|
|
} else {
|
|
preExpression = "(" + nativeType + ") (" + RenderType (underlyingEnumType) + ") ";
|
|
postExpression = string.Empty;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool IsNativeEnum (Type enumType, out Type underlyingType, out string nativeType)
|
|
{
|
|
underlyingType = null;
|
|
nativeType = null;
|
|
|
|
if (!enumType.IsEnum)
|
|
return false;
|
|
|
|
var attrib = AttributeManager.GetCustomAttribute<NativeAttribute> (enumType);
|
|
if (attrib is null)
|
|
return false;
|
|
|
|
underlyingType = enumType.GetEnumUnderlyingType ();
|
|
if (underlyingType == TypeManager.System_Int64) {
|
|
nativeType = "IntPtr";
|
|
} else if (underlyingType == TypeManager.System_UInt64) {
|
|
nativeType = "UIntPtr";
|
|
} else {
|
|
throw new BindingException (1026, true, enumType.FullName, "NativeAttribute");
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void DeclareInvoker (MethodInfo mi)
|
|
{
|
|
if (AttributeManager.HasAttribute<WrapAttribute> (mi))
|
|
return;
|
|
|
|
try {
|
|
// arm64 never requires stret, so we'll always need the non-stret variants
|
|
RegisterMethod (false, mi, MakeSig (mi, false), false);
|
|
RegisterMethod (false, mi, MakeSuperSig (mi, false), false);
|
|
|
|
if (CheckNeedStret (mi)) {
|
|
RegisterMethod (true, mi, MakeSig (mi, true), false);
|
|
RegisterMethod (true, mi, MakeSuperSig (mi, true), false);
|
|
|
|
if (AttributeManager.HasAttribute<AlignAttribute> (mi)) {
|
|
RegisterMethod (true, mi, MakeSig (mi, true, true), true);
|
|
RegisterMethod (true, mi, MakeSuperSig (mi, true, true), true);
|
|
}
|
|
}
|
|
} catch (BindingException ex) {
|
|
throw ex;
|
|
}
|
|
}
|
|
static char [] invalid_selector_chars = new char [] { '*', '^', '(', ')' };
|
|
|
|
public ExportAttribute GetExportAttribute (MemberInfo mo)
|
|
{
|
|
string dummy;
|
|
return GetExportAttribute (mo, out dummy);
|
|
}
|
|
|
|
//
|
|
// Either we have an [Export] attribute, or we have a [Wrap] one
|
|
//
|
|
public ExportAttribute GetExportAttribute (MemberInfo mo, out string wrap)
|
|
{
|
|
wrap = null;
|
|
object [] attrs = AttributeManager.GetCustomAttributes<ExportAttribute> (mo);
|
|
if (attrs.Length == 0) {
|
|
attrs = AttributeManager.GetCustomAttributes<WrapAttribute> (mo);
|
|
if (attrs.Length != 0) {
|
|
wrap = ((WrapAttribute) attrs [0]).MethodName;
|
|
return null;
|
|
}
|
|
PropertyInfo pi = mo as PropertyInfo;
|
|
if (pi != null && pi.CanRead) {
|
|
var getter = pi.GetGetMethod (true);
|
|
attrs = AttributeManager.GetCustomAttributes<ExportAttribute> (getter);
|
|
}
|
|
if (attrs.Length == 0)
|
|
return null;
|
|
}
|
|
|
|
var export = (ExportAttribute) attrs [0];
|
|
|
|
if (string.IsNullOrEmpty (export.Selector))
|
|
throw new BindingException (1024, true, mo.DeclaringType.FullName, mo.Name);
|
|
|
|
if (export.Selector.IndexOfAny (invalid_selector_chars) != -1){
|
|
Console.Error.WriteLine ("Export attribute contains invalid selector name: {0}", export.Selector);
|
|
Environment.Exit (1);
|
|
}
|
|
|
|
return export;
|
|
}
|
|
|
|
public ExportAttribute GetSetterExportAttribute (PropertyInfo pinfo)
|
|
{
|
|
var ea = AttributeManager.GetCustomAttribute<ExportAttribute> (pinfo.GetSetMethod ());
|
|
if (ea != null && ea.Selector != null)
|
|
return ea;
|
|
return AttributeManager.GetCustomAttribute<ExportAttribute> (pinfo)?.ToSetter (pinfo);
|
|
}
|
|
|
|
public ExportAttribute GetGetterExportAttribute (PropertyInfo pinfo)
|
|
{
|
|
var ea = AttributeManager.GetCustomAttribute<ExportAttribute> (pinfo.GetGetMethod ());
|
|
if (ea != null && ea.Selector != null)
|
|
return ea;
|
|
return AttributeManager.GetCustomAttribute<ExportAttribute> (pinfo).ToGetter (pinfo);
|
|
}
|
|
|
|
public Generator (BindingTouch binding_touch, NamespaceManager nsm, bool is_public_mode, bool external, bool debug, Type [] types, Type [] strong_dictionaries)
|
|
{
|
|
BindingTouch = binding_touch;
|
|
ns = nsm;
|
|
IsPublicMode = is_public_mode;
|
|
this.external = external;
|
|
this.debug = debug;
|
|
this.types = types;
|
|
this.strong_dictionaries = strong_dictionaries;
|
|
basedir = ".";
|
|
NativeHandleType = binding_touch.IsDotNet ? "NativeHandle" : "IntPtr";
|
|
}
|
|
|
|
bool SkipGenerationOfType (Type t)
|
|
{
|
|
if (t.IsUnavailable (this))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
public void Go ()
|
|
{
|
|
GeneratedTypes = new GeneratedTypes (this);
|
|
|
|
marshal_types.Add (new MarshalType (TypeManager.NSObject, create: "Runtime.GetNSObject (", closingCreate: ")!"));
|
|
marshal_types.Add (new MarshalType (TypeManager.Selector, create: "Selector.FromHandle (", closingCreate: ")!"));
|
|
marshal_types.Add (new MarshalType (TypeManager.BlockLiteral, "BlockLiteral", "{0}", "THIS_IS_BROKEN"));
|
|
if (TypeManager.MusicSequence != null)
|
|
marshal_types.Add (new MarshalType (TypeManager.MusicSequence, create: "global::AudioToolbox.MusicSequence.Lookup ("));
|
|
marshal_types.Add (TypeManager.CGColor);
|
|
marshal_types.Add (TypeManager.CGPath);
|
|
marshal_types.Add (TypeManager.CGGradient);
|
|
marshal_types.Add (TypeManager.CGContext);
|
|
marshal_types.Add (TypeManager.CGPDFDocument);
|
|
marshal_types.Add (TypeManager.CGPDFPage);
|
|
marshal_types.Add (TypeManager.CGImage);
|
|
marshal_types.Add (TypeManager.Class);
|
|
marshal_types.Add (TypeManager.CFRunLoop);
|
|
marshal_types.Add (TypeManager.CGColorSpace);
|
|
marshal_types.Add (TypeManager.CGImageSource);
|
|
marshal_types.Add (TypeManager.DispatchData);
|
|
marshal_types.Add (TypeManager.DispatchQueue);
|
|
marshal_types.Add (TypeManager.Protocol);
|
|
if (Frameworks.HaveCoreMidi)
|
|
marshal_types.Add (TypeManager.MidiEndpoint);
|
|
if (Frameworks.HaveCoreMedia) {
|
|
marshal_types.Add (TypeManager.CMTimebase);
|
|
marshal_types.Add (TypeManager.CMClock);
|
|
}
|
|
marshal_types.Add (TypeManager.NSZone);
|
|
if (Frameworks.HaveOpenGL) {
|
|
marshal_types.Add (TypeManager.CGLContext);
|
|
marshal_types.Add (TypeManager.CGLPixelFormat);
|
|
marshal_types.Add (TypeManager.CVImageBuffer);
|
|
}
|
|
if (Frameworks.HaveMediaToolbox)
|
|
marshal_types.Add (new MarshalType (TypeManager.MTAudioProcessingTap, create: "MediaToolbox.MTAudioProcessingTap.FromHandle("));
|
|
if (Frameworks.HaveAddressBook) {
|
|
marshal_types.Add (TypeManager.ABAddressBook);
|
|
marshal_types.Add (new MarshalType (TypeManager.ABPerson, create: "(ABPerson) ABRecord.FromHandle (", closingCreate: ")!"));
|
|
marshal_types.Add (new MarshalType (TypeManager.ABRecord, create: "ABRecord.FromHandle (", closingCreate: ")!"));
|
|
}
|
|
if (Frameworks.HaveCoreVideo) {
|
|
// owns `false` like ptr ctor https://github.com/xamarin/xamarin-macios/blob/6f68ab6f79c5f1d96d2cbb1e697330623164e46d/src/CoreVideo/CVBuffer.cs#L74-L90
|
|
marshal_types.Add (new MarshalType (TypeManager.CVPixelBuffer, create: "Runtime.GetINativeObject<CVPixelBuffer> (", closingCreate: ", false)!"));
|
|
}
|
|
marshal_types.Add (TypeManager.CGLayer);
|
|
if (Frameworks.HaveCoreMedia)
|
|
marshal_types.Add (TypeManager.CMSampleBuffer);
|
|
|
|
if (Frameworks.HaveCoreVideo) {
|
|
marshal_types.Add (TypeManager.CVImageBuffer);
|
|
marshal_types.Add (TypeManager.CVPixelBufferPool);
|
|
}
|
|
if (Frameworks.HaveAudioUnit)
|
|
marshal_types.Add (TypeManager.AudioComponent);
|
|
if (Frameworks.HaveCoreMedia) {
|
|
marshal_types.Add (new MarshalType (TypeManager.CMFormatDescription, create: "CMFormatDescription.Create (", closingCreate: ")!"));
|
|
marshal_types.Add (TypeManager.CMAudioFormatDescription);
|
|
marshal_types.Add (TypeManager.CMVideoFormatDescription);
|
|
}
|
|
if (Frameworks.HaveAudioUnit)
|
|
marshal_types.Add (TypeManager.AudioUnit);
|
|
marshal_types.Add (TypeManager.SecIdentity);
|
|
marshal_types.Add (TypeManager.SecIdentity2);
|
|
marshal_types.Add (TypeManager.SecTrust);
|
|
marshal_types.Add (TypeManager.SecTrust2);
|
|
marshal_types.Add (TypeManager.SecProtocolOptions);
|
|
marshal_types.Add (TypeManager.SecProtocolMetadata);
|
|
marshal_types.Add (TypeManager.SecAccessControl);
|
|
marshal_types.Add (TypeManager.AudioBuffers);
|
|
if (Frameworks.HaveAudioUnit) {
|
|
marshal_types.Add (TypeManager.AURenderEventEnumerator);
|
|
}
|
|
|
|
m = GetOutputStream ("ObjCRuntime", "Messaging");
|
|
Header (m);
|
|
print (m, "namespace {0} {{", ns.ObjCRuntime);
|
|
print (m, "\tstatic partial class Messaging {");
|
|
|
|
print (m, "\t\tinternal const string LIBOBJC_DYLIB = \"/usr/lib/libobjc.dylib\";\n");
|
|
if (BindThirdPartyLibrary){
|
|
print (m, "\t\tstatic internal System.Reflection.Assembly this_assembly = typeof (Messaging).Assembly;\n");
|
|
// IntPtr_objc_msgSend[Super]: for init
|
|
print (m, "\t\t[DllImport (LIBOBJC_DYLIB, EntryPoint=\"objc_msgSend\")]");
|
|
print (m, "\t\tpublic extern static IntPtr IntPtr_objc_msgSend (IntPtr receiever, IntPtr selector);");
|
|
send_methods ["IntPtr_objc_msgSend"] = "IntPtr_objc_msgSend";
|
|
print (m, "\t\t[DllImport (LIBOBJC_DYLIB, EntryPoint=\"objc_msgSendSuper\")]");
|
|
print (m, "\t\tpublic extern static IntPtr IntPtr_objc_msgSendSuper (IntPtr receiever, IntPtr selector);");
|
|
send_methods ["IntPtr_objc_msgSendSuper"] = "IntPtr_objc_msgSendSuper";
|
|
// IntPtr_objc_msgSendSuper_IntPtr: for initWithCoder:
|
|
print (m, "\t\t[DllImport (LIBOBJC_DYLIB, EntryPoint=\"objc_msgSend\")]");
|
|
print (m, "\t\tpublic extern static IntPtr IntPtr_objc_msgSend_IntPtr (IntPtr receiever, IntPtr selector, IntPtr arg1);");
|
|
send_methods ["IntPtr_objc_msgSend_IntPtr"] = "IntPtr_objc_msgSend_IntPtr";
|
|
print (m, "\t\t[DllImport (LIBOBJC_DYLIB, EntryPoint=\"objc_msgSendSuper\")]");
|
|
print (m, "\t\tpublic extern static IntPtr IntPtr_objc_msgSendSuper_IntPtr (IntPtr receiever, IntPtr selector, IntPtr arg1);");
|
|
send_methods ["IntPtr_objc_msgSendSuper_IntPtr"] = "IntPtr_objc_msgSendSuper_IntPtr";
|
|
}
|
|
|
|
Array.Sort (types, (a, b) => string.CompareOrdinal (a.FullName, b.FullName));
|
|
|
|
foreach (var t in types) {
|
|
// The generator will create special *Appearance types (these are
|
|
// nested classes). If we've bound a type with the same
|
|
// *Appearance name, we can end up in a situation where the csc
|
|
// compiler uses the the type we don't want due to C#'s resolution
|
|
// rules - this happens if the bound *Appearance type is
|
|
// referenced from the containing type of the special *Appearance
|
|
// type. So always reference the bound *Appearance types using
|
|
// global:: syntax.
|
|
if (t.Name.EndsWith ("Appearance", StringComparison.Ordinal))
|
|
types_that_must_always_be_globally_named.Add (t.Name);
|
|
}
|
|
|
|
foreach (Type t in types){
|
|
if (SkipGenerationOfType (t))
|
|
continue;
|
|
|
|
// We call lookup to build the hierarchy graph
|
|
GeneratedTypes.Lookup (t);
|
|
|
|
var tselectors = new List<string> ();
|
|
|
|
foreach (var pi in GetTypeContractProperties (t)){
|
|
if (pi.IsUnavailable (this))
|
|
continue;
|
|
|
|
if (AttributeManager.HasAttribute<IsThreadStaticAttribute> (pi) && !AttributeManager.HasAttribute<StaticAttribute> (pi))
|
|
throw new BindingException (1008, true);
|
|
|
|
string wrapname;
|
|
var export = GetExportAttribute (pi, out wrapname);
|
|
if (export == null){
|
|
if (wrapname != null)
|
|
continue;
|
|
|
|
// Let properties with the [Field] attribute through as well.
|
|
if (AttributeManager.HasAttribute<FieldAttribute> (pi))
|
|
continue;
|
|
|
|
|
|
if (AttributeManager.HasAttribute<CoreImageFilterPropertyAttribute> (pi))
|
|
continue;
|
|
|
|
// Ensure there's a [Wrap] on either (or both) the getter and setter - since we already know there's no [Export]
|
|
var getMethod = pi.GetGetMethod ();
|
|
var hasWrapGet = getMethod != null && AttributeManager.HasAttribute<WrapAttribute> (getMethod);
|
|
var setMethod = pi.GetSetMethod ();
|
|
var hasWrapSet = setMethod != null && AttributeManager.HasAttribute<WrapAttribute> (setMethod);
|
|
if (hasWrapGet || hasWrapSet)
|
|
continue;
|
|
|
|
throw new BindingException (1018, true, t.FullName, pi.Name);
|
|
}
|
|
if (AttributeManager.HasAttribute<StaticAttribute> (pi))
|
|
need_static [t] = true;
|
|
|
|
#if NET
|
|
var is_abstract = false;
|
|
#else
|
|
bool is_abstract = AttributeManager.HasAttribute<AbstractAttribute> (pi) && pi.DeclaringType == t;
|
|
#endif
|
|
|
|
if (pi.CanRead){
|
|
MethodInfo getter = pi.GetGetMethod ();
|
|
BindAttribute ba = GetBindAttribute (getter);
|
|
|
|
if (!is_abstract)
|
|
tselectors.Add (ba != null ? ba.Selector : export.Selector);
|
|
DeclareInvoker (getter);
|
|
}
|
|
|
|
if (pi.CanWrite){
|
|
MethodInfo setter = pi.GetSetMethod ();
|
|
BindAttribute ba = GetBindAttribute (setter);
|
|
var notImpl = AttributeManager.HasAttribute<NotImplementedAttribute> (setter);
|
|
|
|
if (!is_abstract && !notImpl)
|
|
tselectors.Add (ba != null ? ba.Selector : GetSetterExportAttribute (pi).Selector);
|
|
DeclareInvoker (setter);
|
|
}
|
|
}
|
|
|
|
foreach (var mi in GetTypeContractMethods (t)){
|
|
// Skip properties
|
|
if (mi.IsSpecialName)
|
|
continue;
|
|
|
|
if (mi.IsUnavailable (this))
|
|
continue;
|
|
|
|
bool seenAbstract = false;
|
|
bool seenDefaultValue = false;
|
|
bool seenNoDefaultValue = false;
|
|
|
|
foreach (var attr in AttributeManager.GetCustomAttributes<System.Attribute> (mi)) {
|
|
string selector = null;
|
|
ExportAttribute ea = attr as ExportAttribute;
|
|
BindAttribute ba = attr as BindAttribute;
|
|
if (ea != null){
|
|
selector = ea.Selector;
|
|
} else if (ba != null){
|
|
selector = ba.Selector;
|
|
} else if (attr is StaticAttribute){
|
|
need_static [t] = true;
|
|
continue;
|
|
} else if (attr is InternalAttribute || attr is UnifiedInternalAttribute || attr is ProtectedAttribute){
|
|
continue;
|
|
} else if (attr is NeedsAuditAttribute) {
|
|
continue;
|
|
} else if (attr is FactoryAttribute){
|
|
continue;
|
|
} else if (attr is AbstractAttribute){
|
|
if (mi.DeclaringType == t)
|
|
need_abstract [t] = true;
|
|
seenAbstract = true;
|
|
continue;
|
|
} else if (attr is DefaultValueAttribute || attr is DefaultValueFromArgumentAttribute) {
|
|
seenDefaultValue = true;
|
|
continue;
|
|
} else if (attr is NoDefaultValueAttribute) {
|
|
seenNoDefaultValue = true;
|
|
continue;
|
|
} else if (attr is SealedAttribute || attr is EventArgsAttribute || attr is DelegateNameAttribute || attr is EventNameAttribute || attr is IgnoredInDelegateAttribute || attr is ObsoleteAttribute || attr is NewAttribute || attr is PostGetAttribute || attr is NullAllowedAttribute || attr is CheckDisposedAttribute || attr is SnippetAttribute || attr is AppearanceAttribute || attr is ThreadSafeAttribute || attr is AutoreleaseAttribute || attr is EditorBrowsableAttribute || attr is AdviceAttribute || attr is OverrideAttribute || attr is DelegateApiNameAttribute || attr is ForcedTypeAttribute)
|
|
continue;
|
|
else if (attr is MarshalNativeExceptionsAttribute)
|
|
continue;
|
|
else if (attr is WrapAttribute)
|
|
continue;
|
|
else if (attr is AsyncAttribute)
|
|
continue;
|
|
else if (attr is DesignatedInitializerAttribute)
|
|
continue;
|
|
else if (attr is DesignatedDefaultCtorAttribute)
|
|
continue;
|
|
else if (attr is AvailabilityBaseAttribute)
|
|
continue;
|
|
else if (attr is RequiresSuperAttribute)
|
|
continue;
|
|
else if (attr is NoMethodAttribute)
|
|
continue;
|
|
else {
|
|
switch (attr.GetType ().Name) {
|
|
case "PreserveAttribute":
|
|
case "CompilerGeneratedAttribute":
|
|
case "ManualAttribute":
|
|
case "MarshalDirectiveAttribute":
|
|
case "BindingImplAttribute":
|
|
case "XpcInterfaceAttribute":
|
|
case "NativeIntegerAttribute":
|
|
continue;
|
|
default:
|
|
throw new BindingException (1007, true, attr.GetType (), mi.DeclaringType, mi.Name);
|
|
}
|
|
}
|
|
|
|
if (selector == null)
|
|
throw new BindingException (1009, true, mi.DeclaringType, mi.Name);
|
|
|
|
tselectors.Add (selector);
|
|
if (selector_use.ContainsKey (selector)){
|
|
selector_use [selector]++;
|
|
} else
|
|
selector_use [selector] = 1;
|
|
}
|
|
|
|
if (seenNoDefaultValue && seenAbstract)
|
|
throw new BindingException (1019, true, mi.DeclaringType, mi.Name);
|
|
else if (seenNoDefaultValue && seenDefaultValue)
|
|
throw new BindingException (1019, true, mi.DeclaringType, mi.Name);
|
|
|
|
DeclareInvoker (mi);
|
|
}
|
|
|
|
foreach (var pi in t.GatherProperties (BindingFlags.Instance | BindingFlags.Public, this)){
|
|
if (pi.IsUnavailable (this))
|
|
continue;
|
|
|
|
if (AttributeManager.HasAttribute<AbstractAttribute> (pi) && pi.DeclaringType == t)
|
|
need_abstract [t] = true;
|
|
}
|
|
|
|
selectors [t] = tselectors.Distinct ();
|
|
}
|
|
|
|
foreach (Type t in types){
|
|
if (SkipGenerationOfType (t))
|
|
continue;
|
|
|
|
Generate (t);
|
|
}
|
|
|
|
//DumpChildren (0, GeneratedType.Lookup (TypeManager.NSObject));
|
|
|
|
print (m, "\t}\n}");
|
|
m.Close ();
|
|
|
|
// Generate strong argument types
|
|
GenerateStrongDictionaryTypes ();
|
|
|
|
// Generate the event arg mappings
|
|
if (notification_event_arg_types.Count > 0)
|
|
GenerateEventArgsFile ();
|
|
|
|
if (delegate_types.Count > 0)
|
|
GenerateIndirectDelegateFile ();
|
|
|
|
if (trampolines.Count > 0)
|
|
GenerateTrampolines ();
|
|
|
|
if (libraries.Count > 0)
|
|
GenerateLibraryHandles ();
|
|
|
|
ThrowIfExceptions ();
|
|
}
|
|
|
|
void ThrowIfExceptions ()
|
|
{
|
|
if (exceptions.Count > 0)
|
|
throw new AggregateException (exceptions);
|
|
}
|
|
|
|
static string GenerateNSValue (string propertyToCall)
|
|
{
|
|
return "using (var nsv = Runtime.GetNSObject<NSValue> (value)!)\n\treturn nsv." + propertyToCall + ";";
|
|
}
|
|
|
|
static string GenerateNSNumber (string cast, string propertyToCall)
|
|
{
|
|
return "using (var nsn = Runtime.GetNSObject<NSNumber> (value)!)\n\treturn " + cast + "nsn." + propertyToCall + ";";
|
|
}
|
|
|
|
Type GetCorrectGenericType (Type type)
|
|
{
|
|
return type;
|
|
}
|
|
|
|
void GenerateIndirectDelegateFile ()
|
|
{
|
|
sw = GetOutputStream (null, "SupportDelegates");
|
|
Header (sw);
|
|
RenderDelegates (delegate_types);
|
|
sw.Close ();
|
|
}
|
|
|
|
void GenerateTrampolines ()
|
|
{
|
|
sw = GetOutputStream ("ObjCRuntime", "Trampolines");
|
|
|
|
Header (sw);
|
|
print ("namespace ObjCRuntime {"); indent++;
|
|
print ("");
|
|
print_generated_code ();
|
|
print ("static partial class Trampolines {"); indent++;
|
|
|
|
while (trampolines.Count > 0){
|
|
var queue = trampolines.Values.ToArray ();
|
|
trampolines.Clear ();
|
|
|
|
GenerateTrampolinesForQueue (queue);
|
|
}
|
|
indent--; print ("}");
|
|
indent--; print ("}");
|
|
sw.Close ();
|
|
}
|
|
|
|
Dictionary<Type,bool> generated_trampolines = new Dictionary<Type,bool> ();
|
|
|
|
void GenerateTrampolinesForQueue (TrampolineInfo [] queue)
|
|
{
|
|
Array.Sort (queue, (a, b) => string.CompareOrdinal (a.Type.FullName, b.Type.FullName));
|
|
foreach (var ti in queue) {
|
|
if (generated_trampolines.ContainsKey (ti.Type))
|
|
continue;
|
|
generated_trampolines [ti.Type] = true;
|
|
|
|
var mi = ti.Type.GetMethod ("Invoke");
|
|
var parameters = mi.GetParameters ();
|
|
|
|
print ("");
|
|
print ("[UnmanagedFunctionPointerAttribute (CallingConvention.Cdecl)]");
|
|
print ("[UserDelegateType (typeof ({0}))]", ti.UserDelegate);
|
|
print ("internal delegate {0} {1} ({2});", ti.ReturnType, ti.DelegateName, ti.Parameters);
|
|
print ("");
|
|
print ("//\n// This class bridges native block invocations that call into C#\n//");
|
|
print ("static internal class {0} {{", ti.StaticName); indent++;
|
|
// it can't be conditional without fixing https://github.com/mono/linker/issues/516
|
|
// but we have a workaround in place because we can't fix old, binary bindings so...
|
|
// print ("[Preserve (Conditional=true)]");
|
|
// For .NET we fix it using the DynamicDependency attribute below
|
|
print ("static internal readonly {0} Handler = {1};", ti.DelegateName, ti.TrampolineName);
|
|
print ("");
|
|
#if NET
|
|
print ("[Preserve (Conditional = true)]");
|
|
print ("[global::System.Diagnostics.CodeAnalysis.DynamicDependency (\"Handler\")]");
|
|
#endif
|
|
print ("[MonoPInvokeCallback (typeof ({0}))]", ti.DelegateName);
|
|
print ("static unsafe {0} {1} ({2}) {{", ti.ReturnType, ti.TrampolineName, ti.Parameters);
|
|
indent++;
|
|
print ("var descriptor = (BlockLiteral *) block;");
|
|
print ("var del = ({0}) (descriptor->Target);", ti.UserDelegate);
|
|
bool is_void = ti.ReturnType == "void";
|
|
// FIXME: right now we only support 'null' when the delegate does not return a value
|
|
// otherwise we will need to know the default value to be returned (likely uncommon)
|
|
if (is_void) {
|
|
print ("if (del != null)");
|
|
indent++;
|
|
print ("del ({0});", ti.Invoke);
|
|
indent--;
|
|
if (ti.Clear.Length > 0){
|
|
print ("else");
|
|
indent++;
|
|
print (ti.Clear);
|
|
indent--;
|
|
}
|
|
} else {
|
|
if (ti.Convert.Length > 0)
|
|
print (ti.Convert);
|
|
print ("{0} retval = del ({1});", ti.DelegateReturnType, ti.Invoke);
|
|
if (ti.PostConvert.Length > 0)
|
|
print (ti.PostConvert);
|
|
print (ti.ReturnFormat, "retval");
|
|
}
|
|
indent--;
|
|
print ("}");
|
|
indent--;
|
|
print ("}} /* class {0} */", ti.StaticName);
|
|
|
|
//
|
|
// Now generate the class that allows us to invoke a Objective-C block from C#
|
|
//
|
|
print ("");
|
|
print ($"internal sealed class {ti.NativeInvokerName} : TrampolineBlockBase {{");
|
|
indent++;
|
|
print ("{0} invoker;", ti.DelegateName);
|
|
print ("");
|
|
print_generated_code ();
|
|
print ("public unsafe {0} (BlockLiteral *block) : base (block)", ti.NativeInvokerName);
|
|
print ("{"); indent++;
|
|
print ("invoker = block->GetDelegateForBlock<{0}> ();", ti.DelegateName);
|
|
indent--; print ("}");
|
|
print ("");
|
|
print ("[Preserve (Conditional=true)]");
|
|
print_generated_code ();
|
|
print ("public unsafe static {0}? Create (IntPtr block)\n{{", ti.UserDelegate); indent++;
|
|
print ("if (block == IntPtr.Zero)"); indent++;
|
|
print ("return null;"); indent--;
|
|
print ($"var del = ({ti.UserDelegate}) GetExistingManagedDelegate (block);");
|
|
print ($"return del ?? new {ti.NativeInvokerName} ((BlockLiteral *) block).Invoke;");
|
|
indent--;print ("}");
|
|
print ("");
|
|
var string_pars = new StringBuilder ();
|
|
MakeSignatureFromParameterInfo (false, string_pars, mi, declaringType: null, parameters: parameters);
|
|
print_generated_code ();
|
|
print ("unsafe {0} Invoke ({1})", FormatType (null, mi.ReturnType), string_pars.ToString ());
|
|
print ("{"); indent++;
|
|
string cast_a = "", cast_b = "";
|
|
bool use_temp_return;
|
|
|
|
GenerateArgumentChecks (mi, true);
|
|
|
|
StringBuilder args, convs, disposes, by_ref_processing, by_ref_init;
|
|
GenerateTypeLowering (mi,
|
|
null_allowed_override: true,
|
|
castEnum: false,
|
|
args: out args,
|
|
convs: out convs,
|
|
disposes: out disposes,
|
|
by_ref_processing: out by_ref_processing,
|
|
by_ref_init: out by_ref_init);
|
|
|
|
if (by_ref_init.Length > 0)
|
|
print (by_ref_init.ToString ());
|
|
|
|
use_temp_return = mi.ReturnType != TypeManager.System_Void;
|
|
if (use_temp_return)
|
|
GetReturnsWrappers (mi, null, null, out cast_a, out cast_b);
|
|
|
|
if (convs.Length > 0)
|
|
print (convs.ToString ());
|
|
print ("{0}{1}invoker (BlockPointer{2}){3};",
|
|
use_temp_return ? "var ret = " : "",
|
|
cast_a,
|
|
args.ToString (),
|
|
cast_b);
|
|
if (disposes.Length > 0)
|
|
print (disposes.ToString ());
|
|
if (by_ref_processing.Length > 0)
|
|
print (sw, by_ref_processing.ToString ());
|
|
if (use_temp_return)
|
|
print ("return ret!;");
|
|
indent--; print ("}");
|
|
indent--;
|
|
print ("}} /* class {0} */", ti.NativeInvokerName);
|
|
}
|
|
}
|
|
|
|
// We need to check against the user using just UIKit (and friends) in the FieldAttribute
|
|
// so we need to reflect the libraries contained in our Constants class and do the mapping
|
|
// we will return the system library path if found
|
|
bool IsNotSystemLibrary (string library_name)
|
|
{
|
|
string library_path = null;
|
|
return TryGetLibraryPath (library_name, ref library_path);
|
|
}
|
|
|
|
bool TryGetLibraryPath (string library_name, ref string library_path)
|
|
{
|
|
var libSuffixedName = $"{library_name}Library";
|
|
var constType = TypeManager.Constants;
|
|
var field = constType.GetFields (BindingFlags.Public | BindingFlags.Static).FirstOrDefault (f => f.Name == libSuffixedName);
|
|
library_path = (string) (field?.GetRawConstantValue ());
|
|
return library_path == null;
|
|
}
|
|
|
|
void GenerateLibraryHandles ()
|
|
{
|
|
sw = GetOutputStream ("ObjCRuntime", "Libraries");
|
|
|
|
Header (sw);
|
|
print ("namespace ObjCRuntime {"); indent++;
|
|
print_generated_code ();
|
|
print ("static partial class Libraries {"); indent++;
|
|
foreach (var library_info in libraries.OrderBy (v => v.Key, StringComparer.Ordinal)) {
|
|
var library_name = library_info.Key;
|
|
var library_path = library_info.Value;
|
|
print ("static public class {0} {{", library_name.Replace (".", string.Empty)); indent++;
|
|
if (BindThirdPartyLibrary && library_name == "__Internal") {
|
|
print ("static public readonly IntPtr Handle = Dlfcn.dlopen (null, 0);");
|
|
} else if (BindThirdPartyLibrary && library_path != null && IsNotSystemLibrary (library_name)) {
|
|
print ($"static public readonly IntPtr Handle = Dlfcn.dlopen (\"{library_path}\", 0);");
|
|
} else if (BindThirdPartyLibrary) {
|
|
print ("static public readonly IntPtr Handle = Dlfcn.dlopen (Constants.{0}Library, 0);", library_name);
|
|
} else {
|
|
// Skip the path check that our managed `dlopen` method does
|
|
// This is not required since the path is checked by `IsNotSystemLibrary`
|
|
print ("static public readonly IntPtr Handle = Dlfcn._dlopen (Constants.{0}Library, 0);", library_name);
|
|
}
|
|
indent--; print ("}");
|
|
}
|
|
indent--; print ("}");
|
|
indent--; print ("}");
|
|
sw.Close ();
|
|
}
|
|
|
|
//
|
|
// Processes the various StrongDictionaryAttribute interfaces
|
|
//
|
|
void GenerateStrongDictionaryTypes ()
|
|
{
|
|
foreach (var dictType in strong_dictionaries){
|
|
if (dictType.IsUnavailable (this))
|
|
continue;
|
|
var sa = AttributeManager.GetCustomAttribute<StrongDictionaryAttribute> (dictType);
|
|
var keyContainerType = sa.TypeWithKeys;
|
|
string suffix = sa.Suffix;
|
|
|
|
using (var sw = GetOutputStreamForType (dictType)) {
|
|
string typeName = dictType.Name;
|
|
this.sw = sw;
|
|
Header (sw);
|
|
|
|
print ("namespace {0} {{", dictType.Namespace);
|
|
indent++;
|
|
PrintPlatformAttributes (dictType);
|
|
print ("public partial class {0} : DictionaryContainer {{", typeName);
|
|
indent++;
|
|
sw.WriteLine ("#if !COREBUILD");
|
|
print ("[Preserve (Conditional = true)]");
|
|
print ("public {0} () : base (new NSMutableDictionary ()) {{}}\n", typeName);
|
|
print ("[Preserve (Conditional = true)]");
|
|
print ("public {0} (NSDictionary? dictionary) : base (dictionary) {{}}\n", typeName);
|
|
|
|
foreach (var pi in dictType.GatherProperties (this)) {
|
|
if (pi.IsUnavailable (this))
|
|
continue;
|
|
|
|
string keyname;
|
|
var attrs = AttributeManager.GetCustomAttributes<ExportAttribute> (pi);
|
|
if (attrs.Length == 0)
|
|
keyname = keyContainerType + "." + pi.Name + suffix + "!";
|
|
else {
|
|
keyname = attrs [0].Selector;
|
|
if (keyname.IndexOf ('.') == -1)
|
|
keyname = keyContainerType + "." + keyname + "!";
|
|
}
|
|
|
|
PrintPlatformAttributes (pi);
|
|
string modifier = pi.IsInternal (this) ? "internal" : "public";
|
|
|
|
print ("{0} {1}? {2} {{",
|
|
modifier,
|
|
FormatType (dictType, pi.PropertyType),
|
|
pi.Name);
|
|
|
|
string getter = null, setter = null;
|
|
Type fetchType = pi.PropertyType;
|
|
string castToUnderlying = "";
|
|
string castToEnum = "";
|
|
bool isNativeEnum = false;
|
|
|
|
if (pi.PropertyType.IsEnum){
|
|
fetchType = TypeManager.GetUnderlyingEnumType (pi.PropertyType);
|
|
castToUnderlying = "(" + fetchType + "?)";
|
|
castToEnum = "(" + FormatType (dictType, pi.PropertyType) + "?)";
|
|
isNativeEnum = IsNativeEnum (pi.PropertyType);
|
|
}
|
|
if (pi.PropertyType.IsValueType){
|
|
if (pi.PropertyType == TypeManager.System_Boolean){
|
|
getter = "{1} GetBoolValue ({0})";
|
|
setter = "SetBooleanValue ({0}, {1}value)";
|
|
} else if (fetchType == TypeManager.System_Int32){
|
|
getter = "{1} GetInt32Value ({0})";
|
|
setter = "SetNumberValue ({0}, {1}value)";
|
|
} else if (fetchType == TypeManager.System_nint){
|
|
getter = "{1} GetNIntValue ({0})";
|
|
setter = "SetNumberValue ({0}, {1}value)";
|
|
} else if (fetchType == TypeManager.System_Int64){
|
|
getter = "{1} GetLongValue ({0})";
|
|
setter = "SetNumberValue ({0}, {1}value)";
|
|
} else if (pi.PropertyType == TypeManager.System_Float){
|
|
getter = "{1} GetFloatValue ({0})";
|
|
setter = "SetNumberValue ({0}, {1}value)";
|
|
} else if (pi.PropertyType == TypeManager.System_Double){
|
|
getter = "{1} GetDoubleValue ({0})";
|
|
setter = "SetNumberValue ({0}, {1}value)";
|
|
} else if (fetchType == TypeManager.System_UInt32){
|
|
getter = "{1} GetUInt32Value ({0})";
|
|
setter = "SetNumberValue ({0}, {1}value)";
|
|
} else if (fetchType == TypeManager.System_nuint){
|
|
getter = "{1} GetNUIntValue ({0})";
|
|
setter = "SetNumberValue ({0}, {1}value)";
|
|
} else if (fetchType == TypeManager.CoreGraphics_CGRect){
|
|
getter = "{1} GetCGRectValue ({0})";
|
|
setter = "SetCGRectValue ({0}, {1}value)";
|
|
} else if (fetchType == TypeManager.CoreGraphics_CGSize){
|
|
getter = "{1} GetCGSizeValue ({0})";
|
|
setter = "SetCGSizeValue ({0}, {1}value)";
|
|
} else if (fetchType == TypeManager.CoreGraphics_CGPoint){
|
|
getter = "{1} GetCGPointValue ({0})";
|
|
setter = "SetCGPointValue ({0}, {1}value)";
|
|
} else if (Frameworks.HaveCoreMedia && fetchType == TypeManager.CMTime){
|
|
getter = "{1} GetCMTimeValue ({0})";
|
|
setter = "SetCMTimeValue ({0}, {1}value)";
|
|
} else if (isNativeEnum && fetchType == TypeManager.System_Int64) {
|
|
getter = "{1} (long?) GetNIntValue ({0})";
|
|
setter = "SetNumberValue ({0}, {1}value)";
|
|
} else if (isNativeEnum && fetchType == TypeManager.System_UInt64) {
|
|
getter = "{1} (ulong?) GetNUIntValue ({0})";
|
|
setter = "SetNumberValue ({0}, {1}value)";
|
|
} else {
|
|
throw new BindingException (1033, true, pi.PropertyType, dictType, pi.Name);
|
|
}
|
|
} else {
|
|
if (pi.PropertyType.IsArray){
|
|
var elementType = pi.PropertyType.GetElementType ();
|
|
if (IsWrappedType (elementType)){
|
|
getter = "GetArray<" + FormatType (dictType, elementType) + "> ({0})";
|
|
setter = "SetArrayValue ({0}, value)";
|
|
} else if (elementType.IsEnum){
|
|
// Handle arrays of enums as arrays of NSNumbers, casted to the enum
|
|
var enumTypeStr = FormatType (null, elementType);
|
|
getter = "GetArray<" + enumTypeStr +
|
|
"> ({0}, (ptr)=> {{\n\tusing (var num = Runtime.GetNSObject<NSNumber> (ptr)!){{\n\t\treturn (" +
|
|
enumTypeStr + ") num.Int32Value;\n\t}}\n}})";
|
|
setter = "SetArrayValue<" + enumTypeStr + "> ({0}, value)";
|
|
} else if (elementType == TypeManager.System_String){
|
|
getter = "GetArray<string> ({0}, (ptr) => CFString.FromHandle (ptr)!)";
|
|
setter = "SetArrayValue ({0}, value)";
|
|
} else {
|
|
throw new BindingException (1033, true, pi.PropertyType, dictType, pi.Name);
|
|
}
|
|
} else if (pi.PropertyType == TypeManager.NSString){
|
|
getter = "GetNSStringValue ({0})";
|
|
setter = "SetStringValue ({0}, value)";
|
|
} else if (pi.PropertyType == TypeManager.System_String){
|
|
getter = "GetStringValue ({0})";
|
|
setter = "SetStringValue ({0}, value)";
|
|
} else if (pi.PropertyType.Name.StartsWith ("NSDictionary", StringComparison.Ordinal)){
|
|
if (pi.PropertyType.IsGenericType) {
|
|
var genericParameters = pi.PropertyType.GetGenericArguments ();
|
|
// we want to keep {0} for later yet add the params for the template.
|
|
getter = $"GetNSDictionary <{FormatType (dictType, genericParameters [0])}, {FormatType (dictType, genericParameters [1])}> " + "({0})";
|
|
} else {
|
|
getter = "GetNSDictionary ({0})";
|
|
}
|
|
setter = "SetNativeValue ({0}, value)";
|
|
} else if (IsDictionaryContainerType (pi.PropertyType) || AttributeManager.HasAttribute<StrongDictionaryAttribute> (pi)) {
|
|
var strType = pi.PropertyType.Name;
|
|
getter = "GetStrongDictionary<" + strType + ">({0})";
|
|
setter = "SetNativeValue ({0}, value.GetDictionary ())";
|
|
} else if (IsWrappedType (pi.PropertyType)){
|
|
getter = "Dictionary [{0}] as " + pi.PropertyType;
|
|
setter = "SetNativeValue ({0}, value)";
|
|
} else if (pi.PropertyType.Name == "CGColorSpace") {
|
|
getter = "GetNativeValue<" + pi.PropertyType +"> ({0})";
|
|
setter = "SetNativeValue ({0}, value)";
|
|
} else if (pi.PropertyType.Name == "CGImageSource") {
|
|
getter = "GetNativeValue<" + pi.PropertyType +"> ({0})";
|
|
setter = "SetNativeValue ({0}, value)";
|
|
} else {
|
|
throw new BindingException (1033, true, pi.PropertyType, dictType, pi.Name);
|
|
}
|
|
}
|
|
|
|
var og = getter;
|
|
try {
|
|
getter = String.Format (getter, keyname, castToEnum);
|
|
} catch {
|
|
Console.WriteLine ("OOPS: g={0} k={1} c={2}", og, keyname, castToEnum);
|
|
throw;
|
|
}
|
|
setter = String.Format (setter, keyname, castToUnderlying);
|
|
if (pi.CanRead){
|
|
indent++;
|
|
print ("get {"); indent++;
|
|
print ("return {0};", getter);
|
|
indent--; print ("}");
|
|
indent--;
|
|
}
|
|
if (pi.CanWrite){
|
|
if (setter == null)
|
|
throw new BindingException (1032, true, pi.PropertyType, dictType, pi.Name);
|
|
indent++;
|
|
print ("set {"); indent++;
|
|
print ("{0};", setter);
|
|
indent--; print ("}");
|
|
indent--;
|
|
}
|
|
print ("}\n");
|
|
|
|
}
|
|
sw.WriteLine ("#endif");
|
|
indent--;
|
|
print ("}");
|
|
indent--;
|
|
print ("}");
|
|
}
|
|
}
|
|
}
|
|
|
|
void GenerateEventArgsFile ()
|
|
{
|
|
sw = GetOutputStream ("ObjCRuntime", "EventArgs");
|
|
|
|
Header (sw);
|
|
foreach (Type eventType in notification_event_arg_types.Keys){
|
|
// Do not generate events for stuff with no arguments
|
|
if (eventType == null)
|
|
continue;
|
|
|
|
if (eventType.Namespace != null) {
|
|
print ("namespace {0} {{", eventType.Namespace);
|
|
indent++;
|
|
}
|
|
|
|
print ("public partial class {0} : NSNotificationEventArgs {{", eventType.Name); indent++;
|
|
print ("public {0} (NSNotification notification) : base (notification) \n{{\n}}\n", eventType.Name);
|
|
int i = 0;
|
|
foreach (var prop in eventType.GetProperties (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)){
|
|
if (prop.IsUnavailable (this))
|
|
continue;
|
|
var attrs = AttributeManager.GetCustomAttributes<ExportAttribute> (prop);
|
|
if (attrs.Length == 0)
|
|
throw new BindingException (1010, true, eventType, prop.Name);
|
|
|
|
var is_internal = prop.IsInternal (this);
|
|
var export = attrs [0];
|
|
var use_export_as_string_constant = export.ArgumentSemantic != ArgumentSemantic.None;
|
|
var null_allowed = AttributeManager.HasAttribute<NullAllowedAttribute> (prop);
|
|
var nullable_type = prop.PropertyType.IsValueType && null_allowed;
|
|
var propertyType = prop.PropertyType;
|
|
var propNamespace = prop.DeclaringType.Namespace;
|
|
var probe_presence = AttributeManager.HasAttribute<ProbePresenceAttribute> (prop);
|
|
|
|
string kn = "k" + (i++);
|
|
if (use_export_as_string_constant){
|
|
print ("{0} {1}{2} {3} {{\n\tget {{\n",
|
|
is_internal ? "internal" : "public",
|
|
propertyType,
|
|
nullable_type ? "?" : "",
|
|
prop.Name);
|
|
indent += 2;
|
|
print ("{0} value;", NativeHandleType);
|
|
print ("using (var str = new NSString (\"{0}\")){{", export.Selector);
|
|
kn = "str.Handle";
|
|
indent++;
|
|
} else {
|
|
var lib = propNamespace.Substring (propNamespace.IndexOf ('.') + 1);
|
|
print ("[Field (\"{0}\", \"{1}\")]", export.Selector, lib);
|
|
print ("static IntPtr {0};", kn);
|
|
print ("");
|
|
// linker will remove the attributes (but it's useful for testing)
|
|
print_generated_code ();
|
|
print ("{0} {1}{2} {3} {{",
|
|
is_internal ? "internal" : "public",
|
|
propertyType,
|
|
nullable_type ? "?" : "",
|
|
prop.Name);
|
|
indent++;
|
|
print ("get {");
|
|
indent++;
|
|
print ("{0} value;", NativeHandleType);
|
|
print ("if ({0} == IntPtr.Zero)", kn);
|
|
indent++;
|
|
var libname = BindThirdPartyLibrary ? "__Internal" : lib;
|
|
print ("{0} = ObjCRuntime.Dlfcn.GetIntPtr (Libraries.{1}.Handle, \"{2}\");", kn, libname, export.Selector);
|
|
indent--;
|
|
}
|
|
// [NullAllowed] on `UserInfo` requires a check for every case
|
|
print ("var userinfo = Notification.UserInfo;");
|
|
print ("if (userinfo is null)");
|
|
indent++;
|
|
if (probe_presence)
|
|
print ("return false;");
|
|
else if (null_allowed)
|
|
print ("return null!;");
|
|
else
|
|
print ("value = IntPtr.Zero;");
|
|
indent--;
|
|
print ("else");
|
|
indent++;
|
|
print ("value = userinfo.LowlevelObjectForKey ({0});", kn);
|
|
indent--;
|
|
if (use_export_as_string_constant){
|
|
indent--;
|
|
print ("}");
|
|
} else
|
|
print ("");
|
|
|
|
if (probe_presence)
|
|
print ("return value != IntPtr.Zero;");
|
|
else {
|
|
var et = propertyType.GetElementType ();
|
|
bool is_property_array_wrapped_type = propertyType.IsArray && IsWrappedType (et);
|
|
bool is_property_wrapped_type = IsWrappedType (propertyType);
|
|
bool is_system_string = (propertyType == TypeManager.System_String);
|
|
bool skip_null_check = is_property_wrapped_type || is_property_array_wrapped_type || is_system_string;
|
|
|
|
if (null_allowed && !skip_null_check)
|
|
print ("if (value == IntPtr.Zero)\n\treturn null;");
|
|
else if (propertyType.IsArray)
|
|
print ("if (value == IntPtr.Zero)\n\treturn Array.Empty<{0}> ();", RenderType (et));
|
|
else if (!skip_null_check)
|
|
print ("if (value == IntPtr.Zero)\n\treturn default({0});", RenderType (propertyType));
|
|
|
|
var fullname = propertyType.FullName;
|
|
|
|
if (is_property_array_wrapped_type) {
|
|
print ("return CFArray.ArrayFromHandle<{0}> (value)!;", RenderType (et));
|
|
} else if (is_property_wrapped_type) {
|
|
print ("return Runtime.GetNSObject<{0}> (value)!;", RenderType (propertyType));
|
|
} else if (propertyType == TypeManager.System_Double)
|
|
print (GenerateNSNumber ("", "DoubleValue"));
|
|
else if (propertyType == TypeManager.System_Float)
|
|
print (GenerateNSNumber ("", "FloatValue"));
|
|
else if (fullname == "System.Drawing.PointF")
|
|
print (GenerateNSValue ("PointFValue"));
|
|
else if (fullname == "System.Drawing.SizeF")
|
|
print (GenerateNSValue ("SizeFValue"));
|
|
else if (fullname == "System.Drawing.RectangleF")
|
|
print (GenerateNSValue ("RectangleFValue"));
|
|
else if (fullname == "CoreGraphics.CGPoint")
|
|
print (GenerateNSValue ("CGPointValue"));
|
|
else if (fullname == "CoreGraphics.CGSize")
|
|
print (GenerateNSValue ("CGSizeValue"));
|
|
else if (fullname == "CoreGraphics.CGRect")
|
|
print (GenerateNSValue ("CGRectValue"));
|
|
else if (is_system_string)
|
|
print ("return CFString.FromHandle (value)!;");
|
|
else if (propertyType == TypeManager.NSString)
|
|
print ("return new NSString (value);");
|
|
else if (propertyType == TypeManager.System_String_Array){
|
|
print ("return CFArray.StringArrayFromHandle (value)!;");
|
|
} else {
|
|
Type underlying = propertyType.IsEnum ? TypeManager.GetUnderlyingEnumType (propertyType) : propertyType;
|
|
string cast = propertyType.IsEnum ? "(" + propertyType.FullName + ") " : "";
|
|
|
|
if (underlying == TypeManager.System_Int32)
|
|
print (GenerateNSNumber (cast, "Int32Value"));
|
|
else if (underlying == TypeManager.System_UInt32)
|
|
print (GenerateNSNumber (cast, "UInt32Value"));
|
|
else if (underlying == TypeManager.System_Int64)
|
|
print (GenerateNSNumber (cast, "Int64Value"));
|
|
else if (underlying == TypeManager.System_UInt64)
|
|
print (GenerateNSNumber (cast, "UInt64Value"));
|
|
else if (underlying == TypeManager.System_Int16)
|
|
print (GenerateNSNumber (cast, "Int16Value"));
|
|
else if (underlying == TypeManager.System_UInt16)
|
|
print (GenerateNSNumber (cast, "UInt16Value"));
|
|
else if (underlying == TypeManager.System_SByte)
|
|
print (GenerateNSNumber (cast, "SByteValue"));
|
|
else if (underlying == TypeManager.System_Byte)
|
|
print (GenerateNSNumber (cast, "ByteValue"));
|
|
else if (underlying == TypeManager.System_Boolean)
|
|
print (GenerateNSNumber (cast, "BoolValue"));
|
|
else if (underlying == TypeManager.System_nint)
|
|
print (GenerateNSNumber (cast, "NIntValue"));
|
|
else if (underlying == TypeManager.System_nuint)
|
|
print (GenerateNSNumber (cast, "NUIntValue"));
|
|
else
|
|
throw new BindingException (1011, true, propertyType, underlying);
|
|
}
|
|
}
|
|
indent -= 2;
|
|
print ("\t}\n}\n");
|
|
}
|
|
|
|
indent--; print ("}");
|
|
|
|
if (eventType.Namespace != null) {
|
|
indent--;
|
|
print ("}");
|
|
}
|
|
}
|
|
sw.Close ();
|
|
}
|
|
|
|
|
|
public void DumpChildren (int level, GeneratedType gt)
|
|
{
|
|
string prefix = new string ('\t', level);
|
|
Console.WriteLine ("{2} {0} - {1}", gt.Type.Name, gt.ImplementsAppearance ? "APPEARANCE" : "", prefix);
|
|
foreach (var c in (from s in gt.Children .OrderBy ( s => s.Type.FullName, StringComparer.Ordinal) select s))
|
|
DumpChildren (level+1, c);
|
|
}
|
|
|
|
// this attribute allows the linker to be more clever in removing unused code in bindings - without risking breaking user code
|
|
// only generate those for monotouch now since we can ensure they will be linked away before reaching the devices
|
|
public void GeneratedCode (StreamWriter sw, int tabs, bool optimizable = true)
|
|
{
|
|
for (int i=0; i < tabs; i++)
|
|
sw.Write ('\t');
|
|
sw.Write ("[BindingImpl (BindingImplOptions.GeneratedCode");
|
|
if (optimizable)
|
|
sw.Write (" | BindingImplOptions.Optimizable");
|
|
sw.WriteLine (")]");
|
|
}
|
|
|
|
static void WriteIsDirectBindingCondition (StreamWriter sw, ref int tabs, bool? is_direct_binding, string is_direct_binding_value, Func<string> trueCode, Func<string> falseCode)
|
|
{
|
|
if (is_direct_binding_value != null)
|
|
sw.Write ('\t', tabs).WriteLine ("IsDirectBinding = {0};", is_direct_binding_value);
|
|
|
|
// If we don't know the IsDirectBinding value, we need the condition
|
|
if (!is_direct_binding.HasValue) {
|
|
sw.Write ('\t', tabs).WriteLine ("if (IsDirectBinding) {");
|
|
tabs++;
|
|
}
|
|
|
|
// We need the true part if we don't know, or if we know it's true
|
|
if (is_direct_binding != false) {
|
|
var code = trueCode ();
|
|
if (!string.IsNullOrEmpty (code))
|
|
sw.Write ('\t', tabs).WriteLine (code);
|
|
}
|
|
|
|
if (!is_direct_binding.HasValue)
|
|
sw.Write ('\t', tabs - 1).WriteLine ("} else {");
|
|
|
|
// We need the false part if we don't know, or if we know it's false
|
|
if (is_direct_binding != true) {
|
|
var code = falseCode ();
|
|
if (!string.IsNullOrEmpty (code))
|
|
sw.Write ('\t', tabs).WriteLine (code);
|
|
}
|
|
|
|
if (!is_direct_binding.HasValue) {
|
|
tabs--;
|
|
sw.Write ('\t', tabs).WriteLine ("}");
|
|
}
|
|
}
|
|
|
|
public void print_generated_code (bool optimizable = true)
|
|
{
|
|
GeneratedCode (sw, indent, optimizable);
|
|
}
|
|
|
|
public void print (string format)
|
|
{
|
|
print (sw, format);
|
|
}
|
|
|
|
public void print (string format, params object [] args)
|
|
{
|
|
print (sw, format, args);
|
|
}
|
|
|
|
static char [] newlineCharacters = new char [] { '\n' };
|
|
|
|
public void print (StreamWriter w, string format)
|
|
{
|
|
if (indent < 0)
|
|
throw new InvalidOperationException ("Indent is a negative value.");
|
|
|
|
var lines = format.Split (newlineCharacters);
|
|
|
|
for (int i = 0; i < lines.Length; i++) {
|
|
if (lines [i].Length == 0)
|
|
continue;
|
|
w.Write ('\t', indent);
|
|
w.WriteLine (lines [i]);
|
|
}
|
|
}
|
|
|
|
public void print (StreamWriter w, string format, params object [] args)
|
|
{
|
|
print (w, string.Format (format, args));
|
|
}
|
|
|
|
public void print (StreamWriter w, IEnumerable e)
|
|
{
|
|
foreach (var a in e)
|
|
w.WriteLine (a);
|
|
}
|
|
|
|
bool Duplicated (AvailabilityBaseAttribute candidate, AvailabilityBaseAttribute[] attributes)
|
|
{
|
|
foreach (var a in attributes) {
|
|
if (candidate.AvailabilityKind != a.AvailabilityKind)
|
|
continue;
|
|
if (candidate.Platform != a.Platform)
|
|
continue;
|
|
if (candidate.Version != a.Version)
|
|
continue;
|
|
// the actual message (when present) is not really important
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// do not generate introduced/supported attributes for versions earlier than the minimum supported
|
|
// more important since dotnet and legacy have different minimums (so this can't be done in binding files)
|
|
bool FilterMinimumVersion (AvailabilityBaseAttribute aa)
|
|
{
|
|
#if NET
|
|
// dotnet can never filter minimum versions, as they are semantically important in some cases
|
|
// See for details: https://github.com/xamarin/xamarin-macios/issues/10170
|
|
return true;
|
|
#else
|
|
if (aa.AvailabilityKind != AvailabilityKind.Introduced)
|
|
return true;
|
|
|
|
Version min;
|
|
switch (aa.Platform) {
|
|
case PlatformName.iOS:
|
|
min = Xamarin.SdkVersions.MiniOSVersion;
|
|
break;
|
|
case PlatformName.TvOS:
|
|
min = Xamarin.SdkVersions.MinTVOSVersion;
|
|
break;
|
|
case PlatformName.WatchOS:
|
|
min = Xamarin.SdkVersions.MinWatchOSVersion;
|
|
break;
|
|
case PlatformName.MacOSX:
|
|
min = Xamarin.SdkVersions.MinOSXVersion;
|
|
break;
|
|
case PlatformName.MacCatalyst:
|
|
min = Xamarin.SdkVersions.MinMacCatalystVersion;
|
|
break;
|
|
default:
|
|
throw new BindingException (1047, aa.Platform.ToString ());
|
|
}
|
|
return aa.Version > min;
|
|
#endif
|
|
}
|
|
|
|
static AvailabilityBaseAttribute CloneFromOtherPlatform (AvailabilityBaseAttribute attr, PlatformName platform)
|
|
{
|
|
if (attr.Version is null) {
|
|
switch (attr.AvailabilityKind) {
|
|
case AvailabilityKind.Introduced:
|
|
return new IntroducedAttribute (platform, message: attr.Message);
|
|
case AvailabilityKind.Deprecated:
|
|
return new DeprecatedAttribute(platform, message: attr.Message);
|
|
case AvailabilityKind.Obsoleted:
|
|
return new ObsoletedAttribute(platform, message: attr.Message);
|
|
case AvailabilityKind.Unavailable:
|
|
return new UnavailableAttribute(platform, message: attr.Message);
|
|
default:
|
|
throw new NotImplementedException ();
|
|
}
|
|
}
|
|
else {
|
|
// Revision is optional, and is returned as -1 if not yet. However the Version ctor called inside the attributes throws if you pass -1 so coerse to 0
|
|
int revision = attr.Version.Revision == -1 ? 0 : attr.Version.Revision;
|
|
switch (attr.AvailabilityKind) {
|
|
case AvailabilityKind.Introduced:
|
|
return new IntroducedAttribute(platform, attr.Version.Major, attr.Version.Minor, revision, message: attr.Message);
|
|
case AvailabilityKind.Deprecated:
|
|
return new DeprecatedAttribute(platform, attr.Version.Major, attr.Version.Minor, revision, message: attr.Message);
|
|
case AvailabilityKind.Obsoleted:
|
|
return new ObsoletedAttribute(platform, attr.Version.Major, attr.Version.Minor, revision, message: attr.Message);
|
|
case AvailabilityKind.Unavailable:
|
|
return new UnavailableAttribute(platform, message: attr.Message);
|
|
default:
|
|
throw new NotImplementedException ();
|
|
}
|
|
}
|
|
}
|
|
|
|
static AvailabilityBaseAttribute CreateNoVersionSupportedAttribute (PlatformName platform)
|
|
{
|
|
switch (platform) {
|
|
case PlatformName.iOS:
|
|
case PlatformName.TvOS:
|
|
case PlatformName.MacOSX:
|
|
return new IntroducedAttribute(platform);
|
|
case PlatformName.WatchOS:
|
|
throw new InvalidOperationException ("CreateNoVersionSupportedAttribute for WatchOS never makes sense");
|
|
case PlatformName.MacCatalyst:
|
|
throw new InvalidOperationException ("CreateNoVersionSupportedAttribute for Catalyst never makes sense");
|
|
default:
|
|
throw new NotImplementedException ();
|
|
}
|
|
}
|
|
|
|
static AvailabilityBaseAttribute CreateUnsupportedAttribute (PlatformName platform)
|
|
{
|
|
switch (platform) {
|
|
case PlatformName.iOS:
|
|
case PlatformName.MacCatalyst:
|
|
case PlatformName.MacOSX:
|
|
case PlatformName.TvOS:
|
|
return new UnavailableAttribute(platform);
|
|
case PlatformName.WatchOS:
|
|
throw new InvalidOperationException ("CreateUnsupportedAttribute for WatchOS never makes sense");
|
|
default:
|
|
throw new NotImplementedException ();
|
|
}
|
|
}
|
|
|
|
HashSet<string> GetFrameworkListForPlatform (PlatformName platform)
|
|
{
|
|
HashSet<string> frameworkList = null;
|
|
switch (platform)
|
|
{
|
|
case PlatformName.iOS:
|
|
frameworkList = Frameworks.iosframeworks;
|
|
break;
|
|
case PlatformName.TvOS:
|
|
frameworkList = Frameworks.tvosframeworks;
|
|
break;
|
|
case PlatformName.MacOSX:
|
|
frameworkList = Frameworks.macosframeworks;
|
|
break;
|
|
case PlatformName.MacCatalyst:
|
|
frameworkList = Frameworks.maccatalystframeworks;
|
|
break;
|
|
default:
|
|
frameworkList = new HashSet<string>();
|
|
break;
|
|
}
|
|
return new HashSet<string>(frameworkList.Select(x => x.ToLower (CultureInfo.InvariantCulture)));
|
|
}
|
|
|
|
static string FindNamespace (MemberInfo item)
|
|
{
|
|
switch (item) {
|
|
case TypeInfo type:
|
|
return type.Namespace;
|
|
case PropertyInfo prop:
|
|
return prop.DeclaringType.Namespace;
|
|
case MethodInfo meth:
|
|
return meth.DeclaringType.Namespace;
|
|
default:
|
|
throw new NotImplementedException ($"FindNamespace on {item} of type {item.GetType()}");
|
|
}
|
|
}
|
|
|
|
bool IsInSupportedFramework (MemberInfo klass, PlatformName platform)
|
|
{
|
|
string ns = FindNamespace (klass);
|
|
var list = GetFrameworkListForPlatform (platform);
|
|
return list.Contains(ns.ToLower (CultureInfo.InvariantCulture));
|
|
}
|
|
|
|
void AddUnlistedAvailability (MemberInfo containingClass, List<AvailabilityBaseAttribute> availability)
|
|
{
|
|
// If there are no unavailable attributes for a platform on a type
|
|
// add a supported introduced (without version) since it was "unlisted" (for non-catalyst platforms)
|
|
foreach (var platform in new [] { PlatformName.iOS, PlatformName.TvOS, PlatformName.MacOSX }) {
|
|
if (!PlatformMarkedUnavailable (platform, availability) && IsInSupportedFramework (containingClass, platform)) {
|
|
availability.Add (CreateNoVersionSupportedAttribute (platform));
|
|
}
|
|
}
|
|
}
|
|
|
|
static void AddImpliedCatalyst (List<AvailabilityBaseAttribute> memberAvailability)
|
|
{
|
|
if (!PlatformMarkedUnavailable (PlatformName.MacCatalyst, memberAvailability) &&
|
|
!PlatformHasIntroduced (PlatformName.MacCatalyst, memberAvailability)) {
|
|
foreach (var attr in memberAvailability.Where (v => v.Platform == PlatformName.iOS).ToList()) {
|
|
var newAttribute = CloneFromOtherPlatform (attr, PlatformName.MacCatalyst);
|
|
if (IsValidToCopyTo (memberAvailability, newAttribute, allowIntroducedOnUnavailable: true)) {
|
|
memberAvailability.Add (newAttribute);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool PlatformHasIntroduced (PlatformName platform, List<AvailabilityBaseAttribute> memberAvailability)
|
|
{
|
|
return memberAvailability.Any (v => (v.Platform == platform && v is IntroducedAttribute));
|
|
}
|
|
|
|
static bool PlatformMarkedUnavailable (PlatformName platform, List<AvailabilityBaseAttribute> memberAvailability)
|
|
{
|
|
return memberAvailability.Any (v => (v.Platform == platform && v is UnavailableAttribute));
|
|
}
|
|
|
|
// Especially for TV and Catalyst some entire namespaces are removed via framework_sources.
|
|
// However, almost all of those bindings are [iOS] which AddImpliedCatalyst and other places
|
|
// happily turn into other platforms.
|
|
// As a final step, if we are on a namespace that flatly doesn't exist, drop it. Then if we don't have a not supported, add it
|
|
void StripIntroducedOnNamespaceNotIncluded (List<AvailabilityBaseAttribute> memberAvailability, MemberInfo context)
|
|
{
|
|
if (context is TypeInfo containingClass) {
|
|
var droppedPlatforms = new HashSet<PlatformName>();
|
|
|
|
// Walk all members and look for introduced that are nonsense for our containing class's platform
|
|
foreach (var introduced in memberAvailability.Where (a => a.AvailabilityKind == AvailabilityKind.Introduced).ToList()) {
|
|
// Hack - WebKit namespace has two distinct implementations with different types
|
|
// It can not be hacked in IsInSupportedFramework as AddUnlistedAvailability
|
|
// will add iOS implied to the mac version and so on. So hard code it here...
|
|
if (FindNamespace (containingClass) == "WebKit" && introduced.Platform != PlatformName.TvOS) {
|
|
continue;
|
|
}
|
|
if (!IsInSupportedFramework (containingClass, introduced.Platform)) {
|
|
memberAvailability.Remove (introduced);
|
|
droppedPlatforms.Add (introduced.Platform);
|
|
}
|
|
}
|
|
|
|
// For each attribute we dropped, if we don't have an existing non-introduced, create one
|
|
foreach (var platform in droppedPlatforms) {
|
|
if (!memberAvailability.Any (a => platform == a.Platform && a.AvailabilityKind != AvailabilityKind.Introduced)) {
|
|
memberAvailability.Add (CreateUnsupportedAttribute (platform));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool IsValidToCopyTo (List<AvailabilityBaseAttribute> dest, AvailabilityBaseAttribute addition, bool allowIntroducedOnUnavailable = false)
|
|
{
|
|
// If we are duplicating an existing attribute
|
|
if (dest.Any (d => d.Platform == addition.Platform && d.AvailabilityKind == addition.AvailabilityKind))
|
|
return false;
|
|
// If we are introduced and there is already an Unavailable
|
|
if (!allowIntroducedOnUnavailable && (addition is IntroducedAttribute && dest.Any (d => d.Platform == addition.Platform && d.AvailabilityKind == AvailabilityKind.Unavailable)))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
|
|
static void CopyValidAttributes (List<AvailabilityBaseAttribute> dest, IEnumerable<AvailabilityBaseAttribute> additions)
|
|
{
|
|
foreach (var addition in additions.Where (a => IsValidToCopyTo (dest, a))) {
|
|
dest.Add (CloneFromOtherPlatform (addition, addition.Platform));
|
|
}
|
|
}
|
|
|
|
// Both deprecated and obsolete turn into UnsupportedOSPlatform, so we have to match more generally
|
|
static bool ImpliedKindsMatch (AvailabilityKind left, AvailabilityKind right)
|
|
{
|
|
return ConvertKindToMatchKind (left) == ConvertKindToMatchKind (right);
|
|
}
|
|
|
|
static bool ConvertKindToMatchKind (AvailabilityKind kind)
|
|
{
|
|
switch (kind)
|
|
{
|
|
case AvailabilityKind.Introduced:
|
|
return true;
|
|
case AvailabilityKind.Deprecated:
|
|
case AvailabilityKind.Obsoleted:
|
|
case AvailabilityKind.Unavailable:
|
|
return false;
|
|
default:
|
|
throw new NotImplementedException ($"ConvertKindToMatchKind with unknown kind {kind}");
|
|
}
|
|
}
|
|
|
|
// This assumes the compiler implements property methods as get_ or set_ prefixes
|
|
static PropertyInfo GetProperyFromGetSetMethod (MethodInfo method)
|
|
{
|
|
string name = method.Name;
|
|
if (name.StartsWith ("get_", StringComparison.Ordinal) || name.StartsWith ("set_", StringComparison.Ordinal)) {
|
|
return method.DeclaringType.GetProperty (name.Substring(4));
|
|
}
|
|
return null;
|
|
}
|
|
|
|
static MemberInfo FindContainingContext (MemberInfo mi)
|
|
{
|
|
if (mi is null) {
|
|
throw new InvalidOperationException ("FindContainingContext could not find parent class?");
|
|
}
|
|
if (mi is MethodInfo method) {
|
|
var containingProperty = GetProperyFromGetSetMethod (method);
|
|
if (containingProperty != null) {
|
|
return containingProperty;
|
|
}
|
|
}
|
|
if (mi is TypeInfo) {
|
|
return mi;
|
|
}
|
|
return FindContainingContext (mi.DeclaringType);
|
|
}
|
|
|
|
// We need to collect all of the availability attributes walking up the chain of context.
|
|
// Example: A get_Foo inside of a property Foo which is inside of a class Klass.
|
|
// The Foo property and the Klass both could have unique or duplicate attributes
|
|
// We collect them all, starting with the inner most first in the list.
|
|
// Later on CopyValidAttributes will handle only copying the first valid one down
|
|
List<AvailabilityBaseAttribute> GetAllParentAttributes (MemberInfo context)
|
|
{
|
|
var parentAvailability = new List<AvailabilityBaseAttribute>();
|
|
while (true) {
|
|
parentAvailability.AddRange(AttributeManager.GetCustomAttributes<AvailabilityBaseAttribute> (context));
|
|
var parentContext = FindContainingContext (context);
|
|
if (context == parentContext) {
|
|
return parentAvailability;
|
|
}
|
|
context = parentContext;
|
|
}
|
|
}
|
|
|
|
AvailabilityBaseAttribute [] GetPlatformAttributesToPrint (MemberInfo mi, Type type, MemberInfo inlinedType)
|
|
{
|
|
// Attributes are directly on the member
|
|
List<AvailabilityBaseAttribute> memberAvailability = AttributeManager.GetCustomAttributes<AvailabilityBaseAttribute> (mi).ToList();
|
|
|
|
// Due to differences between Xamarin and NET6 availability attributes, we have to synthesize many duplicates for NET6
|
|
// See https://github.com/xamarin/xamarin-macios/issues/10170 for details
|
|
#if NET
|
|
MemberInfo context = type ?? FindContainingContext (mi);
|
|
// Attributes on the _target_ context, the class itself or the target of the protocol inlining
|
|
List<AvailabilityBaseAttribute> parentContextAvailability = GetAllParentAttributes (context);
|
|
// (Optional) Attributes from the inlined protocol type itself
|
|
List<AvailabilityBaseAttribute> inlinedTypeAvailability = inlinedType != null ? GetAllParentAttributes (inlinedType) : null;
|
|
|
|
// We must consider attributes if we have any on our type, or if we're inlining and that inlined type has attributes
|
|
// If neither are true, we have zero attributes that are relevant
|
|
bool shouldConsiderAttributes = memberAvailability.Any () || inlinedTypeAvailability != null && inlinedTypeAvailability.Any ();
|
|
if (shouldConsiderAttributes) {
|
|
// We will consider any inlinedType attributes first, if any, before any from our parent context
|
|
List<AvailabilityBaseAttribute> availabilityToConsider = new List<AvailabilityBaseAttribute>();
|
|
if (inlinedTypeAvailability != null) {
|
|
availabilityToConsider.AddRange (inlinedTypeAvailability);
|
|
}
|
|
availabilityToConsider.AddRange (parentContextAvailability);
|
|
|
|
// We do not support Watch, so strip from both our input sources before any processing
|
|
memberAvailability = memberAvailability.Where (x => x.Platform != PlatformName.WatchOS).ToList();
|
|
availabilityToConsider = availabilityToConsider.Where (x => x.Platform != PlatformName.WatchOS).ToList();
|
|
|
|
// Add any implied non-catalyst introduced (Catalyst will come later)
|
|
AddUnlistedAvailability (context, availabilityToConsider);
|
|
|
|
// Copy down any unavailable from the parent before expanding, since a [NoMacCatalyst] on the type trumps [iOS] on a member
|
|
CopyValidAttributes (memberAvailability, availabilityToConsider.Where (attr => attr.AvailabilityKind != AvailabilityKind.Introduced));
|
|
|
|
// Add implied catalyst from [iOS] _before_ copying down from parent if no catalyst attributes
|
|
// As those take precedent. We will do this a second time later in a moment..
|
|
AddImpliedCatalyst (memberAvailability);
|
|
|
|
// Now copy it down introduced from the parent
|
|
CopyValidAttributes (memberAvailability, availabilityToConsider.Where (attr => attr.AvailabilityKind == AvailabilityKind.Introduced));
|
|
|
|
// Now expand the implied catalyst from [iOS] a second time
|
|
// This is needed in some cases where the only iOS information is in the
|
|
// parent context, but we want to let any local iOS override a catalyst on the parent
|
|
AddImpliedCatalyst (memberAvailability);
|
|
|
|
if (!BindThirdPartyLibrary) {
|
|
// If all of this implication gives us something silly, like being introduced
|
|
// on a type that is on a namespace we don't support, ignore those Supported
|
|
StripIntroducedOnNamespaceNotIncluded (memberAvailability, context);
|
|
if (inlinedType != null) {
|
|
StripIntroducedOnNamespaceNotIncluded (memberAvailability, inlinedType);
|
|
}
|
|
}
|
|
|
|
// Remove any duplicates attributes as well
|
|
memberAvailability = memberAvailability.Distinct().ToList ();
|
|
}
|
|
#endif
|
|
return memberAvailability.ToArray ();
|
|
}
|
|
|
|
public bool PrintPlatformAttributes (MemberInfo mi, Type type = null, bool is_enum = false)
|
|
{
|
|
bool printed = false;
|
|
if (mi == null)
|
|
return printed;
|
|
|
|
AvailabilityBaseAttribute [] type_ca = null;
|
|
|
|
foreach (var availability in GetPlatformAttributesToPrint (mi, is_enum ? mi.DeclaringType : type, is_enum ? null : mi.DeclaringType)) {
|
|
var t = type ?? (mi as TypeInfo) ?? mi.DeclaringType;
|
|
if (type_ca == null) {
|
|
if (t != null)
|
|
type_ca = AttributeManager.GetCustomAttributes<AvailabilityBaseAttribute> (t);
|
|
else
|
|
type_ca = Array.Empty<AvailabilityBaseAttribute> ();
|
|
}
|
|
#if !NET
|
|
// if we're comparing to something else (than ourself) then don't generate duplicate attributes
|
|
if ((mi != t) && Duplicated (availability, type_ca))
|
|
continue;
|
|
#endif
|
|
switch (availability.AvailabilityKind) {
|
|
case AvailabilityKind.Unavailable:
|
|
// an unavailable member can override type-level attribute
|
|
print (availability.ToString ());
|
|
printed = true;
|
|
break;
|
|
default:
|
|
#if !NET
|
|
// can't introduce or deprecate/obsolete a member on a type that is not available
|
|
if (IsUnavailable (type_ca, availability.Platform))
|
|
continue;
|
|
#endif
|
|
if (FilterMinimumVersion (availability))
|
|
print (availability.ToString ());
|
|
printed = true;
|
|
break;
|
|
}
|
|
}
|
|
return printed;
|
|
}
|
|
|
|
static bool IsUnavailable (IEnumerable<AvailabilityBaseAttribute> customAttributes, PlatformName platform)
|
|
{
|
|
if (customAttributes == null)
|
|
return false;
|
|
foreach (var ca in customAttributes) {
|
|
if ((platform == ca.Platform) && (ca.AvailabilityKind == AvailabilityKind.Unavailable))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool HasAvailability (IEnumerable<AvailabilityBaseAttribute> customAttributes, PlatformName platform)
|
|
{
|
|
if (customAttributes == null)
|
|
return false;
|
|
foreach (var ca in customAttributes) {
|
|
if (platform == ca.Platform)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public void PrintPlatformAttributesNoDuplicates (MemberInfo generatedType, MemberInfo inlinedMethod)
|
|
{
|
|
if ((generatedType == null) || (inlinedMethod == null))
|
|
return;
|
|
|
|
var inlined_ca = new List<AvailabilityBaseAttribute> ();
|
|
inlined_ca.AddRange (GetPlatformAttributesToPrint (inlinedMethod, generatedType.DeclaringType, generatedType));
|
|
if (inlinedMethod.DeclaringType != null) {
|
|
// if not conflictual add the custom attributes from the type
|
|
foreach (var availability in GetPlatformAttributesToPrint (inlinedMethod.DeclaringType, null, generatedType)) {
|
|
// already decorated, skip
|
|
if (HasAvailability (inlined_ca, availability.Platform))
|
|
continue;
|
|
// not available, skip
|
|
if (IsUnavailable (inlined_ca, availability.Platform))
|
|
continue;
|
|
// this type-level custom attribute has meaning and not covered by the method
|
|
inlined_ca.Add (availability);
|
|
}
|
|
}
|
|
|
|
var generated_type_ca = new HashSet<string> ();
|
|
|
|
#if !NET
|
|
foreach (var availability in AttributeManager.GetCustomAttributes<AvailabilityBaseAttribute> (generatedType)) {
|
|
var s = availability.ToString ();
|
|
generated_type_ca.Add (s);
|
|
}
|
|
#endif
|
|
|
|
// the type, in which we are inlining the current method, might already have the same availability attribute
|
|
// which we would duplicate if generated
|
|
foreach (var availability in inlined_ca) {
|
|
if (!FilterMinimumVersion (availability))
|
|
continue;
|
|
var s = availability.ToString ();
|
|
if (!generated_type_ca.Contains (s))
|
|
print (s);
|
|
}
|
|
}
|
|
|
|
public string SelectorField (string s, bool ignore_inline_directive = false)
|
|
{
|
|
string name;
|
|
|
|
if (InlineSelectors && !ignore_inline_directive)
|
|
return "Selector.GetHandle (\"" + s + "\")";
|
|
|
|
if (selector_names.TryGetValue (s, out name))
|
|
return name;
|
|
|
|
StringBuilder sb = new StringBuilder ();
|
|
bool up = true;
|
|
sb.Append ("sel");
|
|
|
|
foreach (char c in s){
|
|
if (up && c != ':'){
|
|
sb.Append (Char.ToUpper (c));
|
|
up = false;
|
|
} else if (c == ':') {
|
|
// Selectors can differ only by colons.
|
|
// Example 'mountDeveloperDiskImageWithError:' and 'mountDeveloperDiskImage:WithError:' (from Xamarin.Hosting)
|
|
// This also showed up in a bug report: http://bugzilla.xamarin.com/show_bug.cgi?id=2626
|
|
// So make sure we create a different name for those in C# as well, otherwise the generated code won't build.
|
|
up = true;
|
|
sb.Append ('_');
|
|
} else
|
|
sb.Append (c);
|
|
}
|
|
if (!InlineSelectors)
|
|
sb.Append ("Handle");
|
|
name = sb.ToString ();
|
|
selector_names [s] = name;
|
|
return name;
|
|
}
|
|
|
|
public string FormatType (Type usedIn, string @namespace, string name)
|
|
{
|
|
string tname;
|
|
if ((usedIn != null && @namespace == usedIn.Namespace) || ns.StandardNamespaces.Contains (@namespace))
|
|
tname = name;
|
|
else
|
|
tname = "global::" + @namespace + "." + name;
|
|
|
|
return tname;
|
|
}
|
|
|
|
// This will return the needed ObjC name for class_ptr lookup
|
|
public string FormatCategoryClassName (BaseTypeAttribute bta)
|
|
{
|
|
object[] attribs;
|
|
|
|
// If we are binding a third party library we need to check for the RegisterAttribute
|
|
// its Name property will contain the propper name we are looking for
|
|
if (BindThirdPartyLibrary) {
|
|
attribs = AttributeManager.GetCustomAttributes<RegisterAttribute> (bta.BaseType);
|
|
if (attribs.Length > 0) {
|
|
var register = (RegisterAttribute)attribs [0];
|
|
return register.Name;
|
|
} else {
|
|
// this will do for categories of third party classes defined in ApiDefinition.cs
|
|
attribs = AttributeManager.GetCustomAttributes<BaseTypeAttribute> (bta.BaseType);
|
|
if (attribs.Length > 0) {
|
|
var baseT = (BaseTypeAttribute)attribs [0];
|
|
if (baseT.Name != null)
|
|
return baseT.Name;
|
|
}
|
|
}
|
|
} else {
|
|
// If we are binding a category inside Xamarin.iOS the selector name will come in
|
|
// the Name property of the BaseTypeAttribute of the base type if changed from the ObjC name
|
|
attribs = AttributeManager.GetCustomAttributes<BaseTypeAttribute> (bta.BaseType);
|
|
if (attribs.Length > 0) {
|
|
var baseT = (BaseTypeAttribute) attribs [0];
|
|
if (baseT.Name != null)
|
|
return baseT.Name;
|
|
}
|
|
}
|
|
|
|
var objcClassName = FormatType (null, bta.BaseType);
|
|
|
|
if (objcClassName.Contains ("global::"))
|
|
objcClassName = objcClassName.Substring (objcClassName.LastIndexOf ('.') + 1);
|
|
|
|
return objcClassName;
|
|
}
|
|
|
|
public string FormatType (Type usedIn, Type type)
|
|
{
|
|
return FormatTypeUsedIn (usedIn == null ? null : usedIn.Namespace, type);
|
|
}
|
|
|
|
public string FormatType (Type usedIn, Type type, bool protocolized)
|
|
{
|
|
return FormatTypeUsedIn (usedIn?.Namespace, type, protocolized);
|
|
}
|
|
|
|
public string FormatTypeUsedIn (string usedInNamespace, Type type, bool protocolized = false)
|
|
{
|
|
type = GetCorrectGenericType (type);
|
|
|
|
if (type == TypeManager.System_Void)
|
|
return "void";
|
|
if (type == TypeManager.System_SByte)
|
|
return "sbyte";
|
|
if (type == TypeManager.System_Int32)
|
|
return "int";
|
|
if (type == TypeManager.System_Int16)
|
|
return "short";
|
|
if (type == TypeManager.System_Int64)
|
|
return "long";
|
|
if (type == TypeManager.System_Byte)
|
|
return "byte";
|
|
if (type == TypeManager.System_UInt16)
|
|
return "ushort";
|
|
if (type == TypeManager.System_UInt32)
|
|
return "uint";
|
|
if (type == TypeManager.System_UInt64)
|
|
return "ulong";
|
|
if (type == TypeManager.System_Byte)
|
|
return "byte";
|
|
if (type == TypeManager.System_Float)
|
|
return "float";
|
|
if (type == TypeManager.System_Double)
|
|
return "double";
|
|
if (type == TypeManager.System_Boolean)
|
|
return "bool";
|
|
if (type == TypeManager.System_String)
|
|
return "string";
|
|
if (type == TypeManager.System_nfloat)
|
|
return "nfloat";
|
|
if (type == TypeManager.System_nint)
|
|
return "nint";
|
|
if (type == TypeManager.System_nuint)
|
|
return "nuint";
|
|
if (type == TypeManager.System_Char)
|
|
return "char";
|
|
if (type == TypeManager.System_nfloat)
|
|
return "nfloat";
|
|
|
|
if (type.IsArray)
|
|
return FormatTypeUsedIn (usedInNamespace, type.GetElementType ()) + "[" + new string (',', type.GetArrayRank () - 1) + "]";
|
|
|
|
var interfaceTag = protocolized == true ? "I" : "";
|
|
string tname;
|
|
// we are adding the usage of ReflectedType just for those cases in which we have nested enums/classes, this soluction does not
|
|
// work with nested/nested/nested classes. But we are not writing a general solution because:
|
|
// 1. We have only encountered nested classes.
|
|
// 2. We are not going to complicate the code more than needed if we have never ever faced a situation with a crazy nested hierarchy,
|
|
// so we only solve the problem we have, no more.
|
|
var parentClass = (type.ReflectedType == null) ? String.Empty : type.ReflectedType.Name + ".";
|
|
if (types_that_must_always_be_globally_named.Contains (type.Name))
|
|
tname = $"global::{type.Namespace}.{parentClass}{interfaceTag}{type.Name}";
|
|
else if ((usedInNamespace != null && type.Namespace == usedInNamespace) || ns.StandardNamespaces.Contains (type.Namespace) || string.IsNullOrEmpty (type.FullName))
|
|
tname = interfaceTag + type.Name;
|
|
else
|
|
tname = $"global::{type.Namespace}.{parentClass}{interfaceTag}{type.Name}";
|
|
|
|
var targs = type.GetGenericArguments ();
|
|
if (targs.Length > 0) {
|
|
var isNullable = TypeManager.GetUnderlyingNullableType (type) != null;
|
|
if (isNullable)
|
|
return FormatTypeUsedIn (usedInNamespace, targs [0]) + "?";
|
|
return RemoveArity (tname) + "<" + string.Join (", ", targs.Select (l => FormatTypeUsedIn (usedInNamespace, l)).ToArray ()) + ">";
|
|
}
|
|
|
|
return tname;
|
|
}
|
|
|
|
static string RemoveArity (string typeName)
|
|
{
|
|
var arity = typeName.IndexOf ('`');
|
|
return arity > 0 ? typeName.Substring (0, arity) : typeName;
|
|
}
|
|
|
|
//
|
|
// Makes the public signature for an exposed method
|
|
//
|
|
public string MakeSignature (MemberInformation minfo)
|
|
{
|
|
return MakeSignature (minfo, false, minfo.method.GetParameters ());
|
|
}
|
|
|
|
//
|
|
// Makes the public signature for an exposed method, taking into account if PreserveAttribute is needed
|
|
//
|
|
public string MakeSignature (MemberInformation minfo, bool alreadyPreserved)
|
|
{
|
|
return MakeSignature (minfo, false, minfo.method.GetParameters (), "", alreadyPreserved);
|
|
}
|
|
|
|
public string GetAsyncName (MethodInfo mi)
|
|
{
|
|
var attr = AttributeManager.GetCustomAttribute<AsyncAttribute> (mi);
|
|
if (attr.MethodName != null)
|
|
return attr.MethodName;
|
|
return mi.Name + "Async";
|
|
}
|
|
|
|
public bool IsModel (Type type)
|
|
{
|
|
return AttributeManager.HasAttribute<ModelAttribute> (type) && !AttributeManager.HasAttribute<SyntheticAttribute> (type);
|
|
}
|
|
|
|
public bool IsProtocol (Type type)
|
|
{
|
|
return AttributeManager.HasAttribute<ProtocolAttribute> (type);
|
|
}
|
|
|
|
public bool Protocolize (ICustomAttributeProvider provider)
|
|
{
|
|
var attribs = AttributeManager.GetCustomAttributes<ProtocolizeAttribute> (provider);
|
|
if (attribs == null || attribs.Length == 0)
|
|
return false;
|
|
|
|
var attrib = attribs [0];
|
|
return XamcoreVersion >= attrib.Version;
|
|
}
|
|
|
|
public string MakeSignature (MemberInformation minfo, bool is_async, ParameterInfo[] parameters, string extra = "", bool alreadyPreserved = false)
|
|
{
|
|
var mi = minfo.method;
|
|
var category_class = minfo.category_extension_type;
|
|
StringBuilder sb = new StringBuilder ();
|
|
string name = minfo.is_ctor ? GetGeneratedTypeName (mi.DeclaringType) : is_async ? GetAsyncName (mi) : mi.Name;
|
|
|
|
// Some codepaths already write preservation info
|
|
PrintAttributes (minfo.mi, preserve:!alreadyPreserved, advice:true, bindAs:true, requiresSuper: true);
|
|
|
|
if (!minfo.is_ctor && !is_async){
|
|
var prefix = "";
|
|
if (!BindThirdPartyLibrary){
|
|
var hasReturnTypeProtocolize = Protocolize (AttributeManager.GetReturnTypeCustomAttributes (minfo.method));
|
|
if (hasReturnTypeProtocolize) {
|
|
if (!IsProtocol (minfo.method.ReturnType)) {
|
|
ErrorHelper.Warning (1108, minfo.method.DeclaringType, minfo.method, minfo.method.ReturnType.FullName);
|
|
} else {
|
|
prefix = "I";
|
|
}
|
|
}
|
|
if (minfo.method.ReturnType.IsArray) {
|
|
var et = minfo.method.ReturnType.GetElementType ();
|
|
if (IsModel (et))
|
|
ErrorHelper.Warning (1109, minfo.method.DeclaringType, minfo.method.Name, et, et.Namespace, et.Name);
|
|
}
|
|
if (IsModel (minfo.method.ReturnType) && !hasReturnTypeProtocolize)
|
|
ErrorHelper.Warning (1107, minfo.method.DeclaringType, minfo.method.Name, minfo.method.ReturnType, minfo.method.ReturnType.Namespace, minfo.method.ReturnType.Name);
|
|
}
|
|
|
|
if (minfo.is_bindAs) {
|
|
if (IsMemberInsideProtocol (minfo.mi.DeclaringType))
|
|
throw new BindingException (1050, true, minfo.mi.DeclaringType.Name);
|
|
|
|
var bindAsAttrib = GetBindAsAttribute (minfo.mi);
|
|
sb.Append (prefix + FormatType (bindAsAttrib.Type.DeclaringType, GetCorrectGenericType (bindAsAttrib.Type)));
|
|
if (!bindAsAttrib.Type.IsValueType && AttributeManager.HasAttribute<NullAllowedAttribute> (mi.ReturnParameter))
|
|
sb.Append ('?');
|
|
} else {
|
|
sb.Append (prefix);
|
|
sb.Append (FormatType (mi.DeclaringType, GetCorrectGenericType (mi.ReturnType)));
|
|
if (!mi.ReturnType.IsValueType && AttributeManager.HasAttribute<NullAllowedAttribute> (mi.ReturnParameter))
|
|
sb.Append ('?');
|
|
}
|
|
|
|
sb.Append (" ");
|
|
}
|
|
// Unified internal methods automatically get a _ appended
|
|
if (minfo.is_extension_method && minfo.method.IsSpecialName) {
|
|
if (name.StartsWith ("get_", StringComparison.Ordinal))
|
|
name = "Get" + name.Substring (4);
|
|
else if (name.StartsWith ("set_", StringComparison.Ordinal))
|
|
name = "Set" + name.Substring (4);
|
|
}
|
|
sb.Append (name);
|
|
if (minfo.is_unified_internal)
|
|
sb.Append ("_");
|
|
sb.Append (" (");
|
|
|
|
bool comma = false;
|
|
if (minfo.is_extension_method) {
|
|
sb.Append ("this ");
|
|
sb.Append ("I" + mi.DeclaringType.Name);
|
|
sb.Append (" This");
|
|
comma = true;
|
|
} else if (category_class != null){
|
|
sb.Append ("this ");
|
|
// Console.WriteLine ("Gto {0} and {1}", mi.DeclaringType, category_class);
|
|
sb.Append (FormatType (mi.DeclaringType, category_class));
|
|
sb.Append (" This");
|
|
comma = true;
|
|
}
|
|
MakeSignatureFromParameterInfo (comma, sb, mi, minfo.type, parameters);
|
|
sb.Append (extra);
|
|
sb.Append (")");
|
|
return sb.ToString ();
|
|
}
|
|
|
|
//
|
|
// Renders the parameters in @parameters in a format suitable for a method declaration.
|
|
// The result is place into the provided string builder
|
|
//
|
|
public void MakeSignatureFromParameterInfo (bool comma, StringBuilder sb, MemberInfo mi, Type declaringType, ParameterInfo [] parameters)
|
|
{
|
|
int parCount = parameters.Length;
|
|
for (int pari = 0; pari < parCount; pari++){
|
|
var pi = parameters [pari];
|
|
|
|
if (comma)
|
|
sb.Append (", ");
|
|
comma = true;
|
|
|
|
// Format nicely the type, as succinctly as possible
|
|
Type parType = GetCorrectGenericType (pi.ParameterType);
|
|
if (parType.IsSubclassOf (TypeManager.System_Delegate)){
|
|
var ti = MakeTrampoline (parType);
|
|
sb.AppendFormat ("[BlockProxy (typeof (ObjCRuntime.Trampolines.{0}))]", ti.NativeInvokerName);
|
|
}
|
|
|
|
if (AttributeManager.HasAttribute<TransientAttribute> (pi))
|
|
sb.Append ("[Transient] ");
|
|
|
|
if (parType.IsByRef){
|
|
string reftype = TypeManager.IsOutParameter (pi) ? "out " : "ref ";
|
|
sb.Append (reftype);
|
|
parType = parType.GetElementType ();
|
|
}
|
|
// note: check for .NET `params` on the bindings, which generates a `ParamArrayAttribute` or the old (not really required) custom `ParamsAttribute`
|
|
if (pari == parCount - 1 && parType.IsArray && (AttributeManager.HasAttribute<ParamsAttribute> (pi) || AttributeManager.HasAttribute<ParamArrayAttribute> (pi))) {
|
|
sb.Append ("params ");
|
|
}
|
|
var protocolized = false;
|
|
if (!BindThirdPartyLibrary && Protocolize (pi)) {
|
|
if (!AttributeManager.HasAttribute<ProtocolAttribute> (parType)) {
|
|
Console.WriteLine ("Protocolized attribute for type that does not have a [Protocol] for {0}'s parameter {1}", mi, pi);
|
|
}
|
|
protocolized = true;
|
|
}
|
|
|
|
var bindAsAtt = GetBindAsAttribute (pi);
|
|
if (bindAsAtt != null) {
|
|
PrintBindAsAttribute (pi, sb);
|
|
var bt = bindAsAtt.Type;
|
|
sb.Append (FormatType (bt.DeclaringType, bt, protocolized));
|
|
if (!bt.IsValueType && AttributeManager.HasAttribute<NullAllowedAttribute> (pi))
|
|
sb.Append ('?');
|
|
} else {
|
|
sb.Append (FormatType (declaringType, parType, protocolized));
|
|
// some `IntPtr` are decorated with `[NullAttribute]`
|
|
if (!parType.IsValueType && AttributeManager.HasAttribute<NullAllowedAttribute> (pi))
|
|
sb.Append ('?');
|
|
}
|
|
|
|
sb.Append (" ");
|
|
sb.Append (pi.Name.GetSafeParamName ());
|
|
}
|
|
}
|
|
|
|
void Header (StreamWriter w)
|
|
{
|
|
print (w, "//\n// Auto-generated from generator.cs, do not edit\n//");
|
|
print (w, "// We keep references to objects, so warning 414 is expected\n");
|
|
print (w, "#pragma warning disable 414\n");
|
|
print (w, ns.ImplicitNamespaces.OrderByDescending (n => n.StartsWith ("System", StringComparison.Ordinal)).ThenBy (n => n.Length).Select (n => "using " + n + ";"));
|
|
print (w, "");
|
|
print (w, "#nullable enable");
|
|
print (w, "");
|
|
print (w, "#if !NET");
|
|
print (w, "using NativeHandle = System.IntPtr;");
|
|
print (w, "#endif");
|
|
}
|
|
|
|
//
|
|
// Given a method info that has a return value, produce the text necessary on
|
|
// both sides of a call to wrap the result into user-facing MonoTouch types.
|
|
//
|
|
// @mi: the method info, should not have a returntype of void
|
|
// @cast_a: left side to generate
|
|
// @cast_b: right side to generate
|
|
//
|
|
void GetReturnsWrappers (MethodInfo mi, MemberInformation minfo, Type declaringType, out string cast_a, out string cast_b, StringBuilder postproc = null)
|
|
{
|
|
cast_a = cast_b = "";
|
|
if (mi.ReturnType == TypeManager.System_Void){
|
|
throw new ArgumentException ("the provided Method has a void return type, it should never call this method");
|
|
}
|
|
|
|
MarshalInfo mai = new MarshalInfo (this, mi);
|
|
MarshalType mt;
|
|
|
|
if (GetNativeEnumToManagedExpression (mi.ReturnType, out cast_a, out cast_b, out var _, postproc)) {
|
|
// we're done here
|
|
} else if (mi.ReturnType.IsEnum){
|
|
cast_a = "(" + FormatType (mi.DeclaringType, mi.ReturnType) + ") ";
|
|
cast_b = "";
|
|
} else if (LookupMarshal (mai.Type, out mt)){
|
|
cast_a = mt.CreateFromRet;
|
|
cast_b = mt.ClosingCreate;
|
|
} else if (IsWrappedType (mi.ReturnType)){
|
|
// protocol support means we can return interfaces and, as far as .NET knows, they might not be NSObject
|
|
if (IsProtocolInterface (mi.ReturnType)) {
|
|
cast_a = " Runtime.GetINativeObject<" + FormatType (mi.DeclaringType, mi.ReturnType) + "> (";
|
|
cast_b = ", false)!";
|
|
} else if (minfo != null && minfo.protocolize) {
|
|
cast_a = " Runtime.GetINativeObject<" + FormatType (mi.DeclaringType, mi.ReturnType.Namespace, FindProtocolInterface (mi.ReturnType, mi)) + "> (";
|
|
cast_b = ", false)!";
|
|
} else if (minfo != null && minfo.is_forced) {
|
|
cast_a = " Runtime.GetINativeObject<" + FormatType (declaringType, GetCorrectGenericType (mi.ReturnType)) + "> (";
|
|
cast_b = $", true, {minfo.is_forced_owns})!";
|
|
} else if (minfo != null && minfo.is_bindAs) {
|
|
var bindAs = GetBindAsAttribute (minfo.mi);
|
|
var nullableBindAsType = TypeManager.GetUnderlyingNullableType (bindAs.Type);
|
|
var isNullable = nullableBindAsType != null;
|
|
var bindAsType = TypeManager.GetUnderlyingNullableType (bindAs.Type) ?? bindAs.Type;
|
|
var formattedBindAsType = FormatType (declaringType, GetCorrectGenericType (bindAs.Type));
|
|
string suffix;
|
|
var wrapper = GetFromBindAsWrapper (minfo, out suffix);
|
|
var formattedReturnType = FormatType (declaringType, GetCorrectGenericType (mi.ReturnType));
|
|
if (mi.ReturnType == TypeManager.NSString) {
|
|
if (isNullable) {
|
|
print ("{0} retvaltmp;", NativeHandleType);
|
|
cast_a = "((retvaltmp = ";
|
|
cast_b = $") == IntPtr.Zero ? default ({formattedBindAsType}) : ({wrapper}Runtime.GetNSObject<{formattedReturnType}> (retvaltmp)!){suffix})";
|
|
} else {
|
|
cast_a = $"{wrapper}Runtime.GetNSObject<{formattedReturnType}> (";
|
|
cast_b = $")!{suffix}";
|
|
}
|
|
} else {
|
|
var enumCast = (bindAsType.IsEnum && !minfo.type.IsArray) ? $"({formattedBindAsType}) " : string.Empty;
|
|
print ("{0} retvaltmp;", NativeHandleType);
|
|
cast_a = "((retvaltmp = ";
|
|
cast_b = $") == IntPtr.Zero ? default ({formattedBindAsType}) : ({enumCast}Runtime.GetNSObject<{formattedReturnType}> (retvaltmp)!{wrapper})){suffix}";
|
|
}
|
|
} else {
|
|
cast_a = " Runtime.GetNSObject<" + FormatType (declaringType, GetCorrectGenericType (mi.ReturnType)) + "> (";
|
|
cast_b = ")!";
|
|
}
|
|
} else if (mi.ReturnType.IsGenericParameter) {
|
|
cast_a = " Runtime.GetINativeObject<" + mi.ReturnType.Name + "> (";
|
|
cast_b = ", false)!";
|
|
} else if (mai.Type == TypeManager.System_String && !mai.PlainString){
|
|
cast_a = "CFString.FromHandle (";
|
|
cast_b = ")!";
|
|
} else if (mi.ReturnType.IsSubclassOf (TypeManager.System_Delegate)){
|
|
cast_a = "";
|
|
cast_b = "";
|
|
} else if (mai.Type.IsArray){
|
|
Type etype = mai.Type.GetElementType ();
|
|
if (minfo != null && minfo.is_bindAs) {
|
|
var bindAttrType = GetBindAsAttribute (minfo.mi).Type;
|
|
if (!bindAttrType.IsArray) {
|
|
throw new BindingException (1071, true, minfo.mi.DeclaringType.FullName, minfo.mi.Name);
|
|
}
|
|
var bindAsT = bindAttrType.GetElementType ();
|
|
var suffix = string.Empty;
|
|
print ("{0} retvalarrtmp;", NativeHandleType);
|
|
cast_a = "((retvalarrtmp = ";
|
|
cast_b = ") == IntPtr.Zero ? null! : (";
|
|
cast_b += $"NSArray.ArrayFromHandleFunc <{FormatType (bindAsT.DeclaringType, bindAsT)}> (retvalarrtmp, {GetFromBindAsWrapper (minfo, out suffix)})" + suffix;
|
|
cast_b += "))";
|
|
} else if (etype == TypeManager.System_String) {
|
|
cast_a = "CFArray.StringArrayFromHandle (";
|
|
cast_b = ")!";
|
|
} else if (minfo != null && minfo.protocolize) {
|
|
cast_a = "CFArray.ArrayFromHandle<global::" + etype.Namespace + ".I" + etype.Name + ">(";
|
|
cast_b = ")!";
|
|
} else if (etype == TypeManager.Selector) {
|
|
exceptions.Add (ErrorHelper.CreateError (1066, mai.Type.FullName, mi.DeclaringType.FullName, mi.Name));
|
|
} else {
|
|
if (NamespaceManager.NamespacesThatConflictWithTypes.Contains (etype.Namespace))
|
|
cast_a = "CFArray.ArrayFromHandle<global::" + etype + ">(";
|
|
else
|
|
cast_a = "CFArray.ArrayFromHandle<" + FormatType (mi.DeclaringType, etype) + ">(";
|
|
cast_b = ")!";
|
|
}
|
|
} else if (mi.ReturnType.Namespace == "System" && mi.ReturnType.Name == "nint") {
|
|
cast_a = "(nint) ";
|
|
} else if (mi.ReturnType.Namespace == "System" && mi.ReturnType.Name == "nuint") {
|
|
cast_a = "(nuint) ";
|
|
}
|
|
}
|
|
|
|
void GenerateInvoke (bool stret, bool supercall, MethodInfo mi, MemberInformation minfo, string selector, string args, bool assign_to_temp, Type category_type, bool aligned)
|
|
{
|
|
string target_name = (category_type == null && !minfo.is_extension_method) ? "this" : "This";
|
|
string handle = supercall ? ".SuperHandle" : ".Handle";
|
|
|
|
// If we have supercall == false, we can be a Bind method that has a [Target]
|
|
if (supercall == false && !minfo.is_static){
|
|
foreach (var pi in mi.GetParameters ()){
|
|
if (IsTarget (pi)){
|
|
if (pi.ParameterType == TypeManager.System_String){
|
|
var mai = new MarshalInfo (this, mi, pi);
|
|
|
|
if (mai.PlainString)
|
|
ErrorHelper.Warning (1101);
|
|
|
|
if (mai.ZeroCopyStringMarshal){
|
|
target_name = "(IntPtr)(&_s" + pi.Name + ")";
|
|
handle = "";
|
|
} else {
|
|
target_name = "ns" + pi.Name;
|
|
handle = "";
|
|
}
|
|
} else
|
|
target_name = pi.Name.GetSafeParamName ();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
string sig = supercall ? MakeSuperSig (mi, stret, aligned) : MakeSig (mi, stret, aligned);
|
|
|
|
sig = "global::" + ns.Messaging + "." + sig;
|
|
|
|
string selector_field;
|
|
if (minfo.is_interface_impl || minfo.is_extension_method) {
|
|
var tmp = InlineSelectors;
|
|
InlineSelectors = true;
|
|
selector_field = SelectorField (selector);
|
|
InlineSelectors = tmp;
|
|
} else {
|
|
selector_field = SelectorField (selector);
|
|
}
|
|
|
|
if (ShouldMarshalNativeExceptions (mi))
|
|
args += ", out exception_gchandle";
|
|
|
|
if (stret){
|
|
string ret_val = aligned ? "aligned_ret" : "out ret";
|
|
if (minfo.is_static)
|
|
print ("{0} ({5}, class_ptr, {3}{4});", sig, "/*unusued*/", "/*unusued*/", selector_field, args, ret_val);
|
|
else
|
|
print ("{0} ({5}, {1}{2}, {3}{4});", sig, target_name, handle, selector_field, args, ret_val);
|
|
|
|
if (aligned)
|
|
print ("aligned_assigned = true;");
|
|
} else {
|
|
bool returns = mi.ReturnType != TypeManager.System_Void && mi.Name != "Constructor";
|
|
string cast_a = "", cast_b = "";
|
|
StringBuilder postproc = new StringBuilder ();
|
|
|
|
if (returns)
|
|
GetReturnsWrappers (mi, minfo, mi.DeclaringType, out cast_a, out cast_b, postproc);
|
|
else if (mi.Name == "Constructor") {
|
|
cast_a = "InitializeHandle (";
|
|
cast_b = ", \"" + selector + "\")";
|
|
}
|
|
|
|
if (minfo.is_static)
|
|
print ("{0}{1}{2} (class_ptr, {5}{6}){7};",
|
|
returns ? (assign_to_temp ? "ret = " : "return ") : "",
|
|
cast_a, sig, target_name,
|
|
"/*unusued3*/", //supercall ? "Super" : "",
|
|
selector_field, args, cast_b);
|
|
else
|
|
print ("{0}{1}{2} ({3}{4}, {5}{6}){7};",
|
|
returns ? (assign_to_temp ? "ret = " : "return ") : "",
|
|
cast_a, sig, target_name,
|
|
handle,
|
|
selector_field, args, cast_b);
|
|
|
|
if (postproc.Length > 0)
|
|
print (postproc.ToString ());
|
|
}
|
|
}
|
|
|
|
void GenerateNewStyleInvoke (bool supercall, MethodInfo mi, MemberInformation minfo, string selector, string args, bool assign_to_temp, Type category_type)
|
|
{
|
|
var returnType = mi.ReturnType;
|
|
bool x64_stret = Stret.X86_64NeedStret (returnType, this);
|
|
bool aligned = AttributeManager.HasAttribute<AlignAttribute> (mi);
|
|
|
|
if (CurrentPlatform == PlatformName.MacOSX || CurrentPlatform == PlatformName.MacCatalyst) {
|
|
if (x64_stret) {
|
|
print ("if (global::ObjCRuntime.Runtime.IsARM64CallingConvention) {");
|
|
indent++;
|
|
GenerateInvoke (false, supercall, mi, minfo, selector, args, assign_to_temp, category_type, false);
|
|
indent--;
|
|
print ("} else {");
|
|
indent++;
|
|
GenerateInvoke (x64_stret, supercall, mi, minfo, selector, args, assign_to_temp, category_type, aligned && x64_stret);
|
|
indent--;
|
|
print ("}");
|
|
} else {
|
|
GenerateInvoke (false, supercall, mi, minfo, selector, args, assign_to_temp, category_type, false);
|
|
}
|
|
return;
|
|
}
|
|
|
|
bool arm_stret = Stret.ArmNeedStret (returnType, this);
|
|
bool x86_stret = Stret.X86NeedStret (returnType, this);
|
|
bool is_stret_multi = arm_stret || x86_stret || x64_stret;
|
|
bool need_multi_path = is_stret_multi;
|
|
|
|
if (need_multi_path) {
|
|
if (is_stret_multi) {
|
|
// First check for arm64
|
|
print ("if (global::ObjCRuntime.Runtime.IsARM64CallingConvention) {");
|
|
indent++;
|
|
GenerateInvoke (false, supercall, mi, minfo, selector, args, assign_to_temp, category_type, false);
|
|
indent--;
|
|
// If we're not arm64, but we're 64-bit, then we're x86_64
|
|
print ("} else if (IntPtr.Size == 8) {");
|
|
indent++;
|
|
GenerateInvoke (x64_stret, supercall, mi, minfo, selector, args, assign_to_temp, category_type, aligned && x64_stret);
|
|
indent--;
|
|
// if we're not 64-bit, but we're on device, then we're 32-bit arm
|
|
print ("} else if (Runtime.Arch == Arch.DEVICE) {");
|
|
indent++;
|
|
GenerateInvoke (arm_stret, supercall, mi, minfo, selector, args, assign_to_temp, category_type, aligned && arm_stret);
|
|
indent--;
|
|
// if we're none of the above, we're x86
|
|
print ("} else {");
|
|
indent++;
|
|
GenerateInvoke (x86_stret, supercall, mi, minfo, selector, args, assign_to_temp, category_type, aligned && x86_stret);
|
|
indent--;
|
|
print ("}");
|
|
} else {
|
|
print ("if (IntPtr.Size == 8) {");
|
|
indent++;
|
|
GenerateInvoke (x64_stret, supercall, mi, minfo, selector, args, assign_to_temp, category_type, aligned && x64_stret);
|
|
indent--;
|
|
print ("} else {");
|
|
indent++;
|
|
GenerateInvoke (x86_stret, supercall, mi, minfo, selector, args, assign_to_temp, category_type, aligned && x86_stret);
|
|
indent--;
|
|
print ("}");
|
|
}
|
|
} else {
|
|
GenerateInvoke (false, supercall, mi, minfo, selector, args, assign_to_temp, category_type, false);
|
|
}
|
|
}
|
|
|
|
static char [] newlineTab = new char [] { '\n', '\t' };
|
|
|
|
void Inject<T> (MethodInfo method) where T: SnippetAttribute
|
|
{
|
|
var snippets = AttributeManager.GetCustomAttributes<T> (method);
|
|
if (snippets.Length == 0)
|
|
return;
|
|
foreach (SnippetAttribute snippet in snippets)
|
|
Inject (snippet);
|
|
}
|
|
|
|
void Inject (SnippetAttribute snippet)
|
|
{
|
|
if (snippet.Code == null)
|
|
return;
|
|
var lines = snippet.Code.Split (newlineTab);
|
|
foreach (var l in lines){
|
|
if (l.Length == 0)
|
|
continue;
|
|
print (l);
|
|
}
|
|
}
|
|
|
|
bool IsOptimizable (MemberInfo method)
|
|
{
|
|
var optimizable = true;
|
|
var snippets = AttributeManager.GetCustomAttributes<SnippetAttribute> (method);
|
|
if (snippets.Length > 0) {
|
|
foreach (SnippetAttribute snippet in snippets)
|
|
optimizable &= snippet.Optimizable;
|
|
}
|
|
return optimizable;
|
|
}
|
|
|
|
[Flags]
|
|
public enum BodyOption {
|
|
None = 0x0,
|
|
NeedsTempReturn = 0x1,
|
|
CondStoreRet = 0x3,
|
|
MarkRetDirty = 0x5,
|
|
StoreRet = 0x7,
|
|
}
|
|
|
|
public enum ThreadCheck {
|
|
Default, // depends on the namespace
|
|
Off,
|
|
On,
|
|
}
|
|
|
|
//
|
|
// generates the code to marshal a string from C# to Objective-C:
|
|
//
|
|
// @probe_null: determines whether null is allowed, and
|
|
// whether we need to generate code to handle this
|
|
//
|
|
// @must_copy: determines whether to create a new NSString, necessary
|
|
// for NSString properties that are flagged with "retain" instead of "copy"
|
|
//
|
|
// @prefix: prefix to prepend on each line
|
|
//
|
|
// @property: the name of the property
|
|
//
|
|
public string GenerateMarshalString (bool probe_null, bool must_copy)
|
|
{
|
|
if (must_copy){
|
|
return "var ns{0} = CFString.CreateNative ({1});\n";
|
|
}
|
|
return
|
|
"ObjCRuntime.NSStringStruct _s{0}; Console.WriteLine (\"" + CurrentMethod + ": Marshalling: {{1}}\", {1}); \n" +
|
|
"_s{0}.ClassPtr = ObjCRuntime.NSStringStruct.ReferencePtr;\n" +
|
|
"_s{0}.Flags = 0x010007d1; // RefCount=1, Unicode, InlineContents = 0, DontFreeContents\n" +
|
|
"_s{0}.UnicodePtr = _p{0};\n" +
|
|
"_s{0}.Length = " + (probe_null ? "{1} is null ? 0 : {1}.Length;" : "{1}.Length;\n");
|
|
}
|
|
|
|
public string GenerateDisposeString (bool probe_null, bool must_copy)
|
|
{
|
|
if (must_copy){
|
|
return "CFString.ReleaseNative (ns{0});\n";
|
|
} else
|
|
return "if (_s{0}.Flags != 0x010007d1) throw new Exception (\"String was retained, not copied\");";
|
|
}
|
|
|
|
List<string> CollectFastStringMarshalParameters (MethodInfo mi)
|
|
{
|
|
List<string> stringParameters = null;
|
|
|
|
foreach (var pi in mi.GetParameters ()){
|
|
var mai = new MarshalInfo (this, mi, pi);
|
|
|
|
if (mai.ZeroCopyStringMarshal){
|
|
if (stringParameters == null)
|
|
stringParameters = new List<string>();
|
|
stringParameters.Add (pi.Name.GetSafeParamName ());
|
|
}
|
|
}
|
|
return stringParameters;
|
|
}
|
|
|
|
AvailabilityBaseAttribute GetIntroduced (Type type, string methodName)
|
|
{
|
|
if (type == null)
|
|
return null;
|
|
|
|
var prop = type.GetProperties ()
|
|
.Where (pi => pi.Name == methodName)
|
|
.FirstOrDefault ();
|
|
|
|
if (prop != null)
|
|
return prop.GetAvailability (AvailabilityKind.Introduced, this);
|
|
|
|
return GetIntroduced (ReflectionExtensions.GetBaseType (type, this), methodName);
|
|
}
|
|
|
|
AvailabilityBaseAttribute GetIntroduced (MethodInfo mi, PropertyInfo pi)
|
|
{
|
|
return mi.GetAvailability (AvailabilityKind.Introduced, this) ?? pi.GetAvailability (AvailabilityKind.Introduced, this);
|
|
}
|
|
|
|
bool Is64BitiOSOnly (ICustomAttributeProvider provider)
|
|
{
|
|
if (BindThirdPartyLibrary)
|
|
return false;
|
|
if (BindingTouch.CurrentPlatform != PlatformName.iOS)
|
|
return false;
|
|
var attrib = provider.GetAvailability (AvailabilityKind.Introduced, this);
|
|
if (attrib == null) {
|
|
var minfo = provider as MemberInfo;
|
|
if (minfo != null && minfo.DeclaringType != null)
|
|
return Is64BitiOSOnly (minfo.DeclaringType);
|
|
return false;
|
|
}
|
|
return attrib.Version?.Major >= 11;
|
|
}
|
|
|
|
//
|
|
// Generates the code necessary to lower the MonoTouch-APIs to something suitable
|
|
// to be passed to Objective-C.
|
|
//
|
|
// This turns things like strings into NSStrings, NSObjects into the Handle object,
|
|
// INativeObjects into calling the handle and so on.
|
|
//
|
|
// The result is delivered as a StringBuilder that is used to prepare the marshaling
|
|
// and then one that must be executed after the method has been invoked to cleaup the
|
|
// results
|
|
//
|
|
// @mi: input parameter, contains the method info with the C# signature to generate lowering for
|
|
// @null_allowed_override: this is suitable for applying [NullAllowed] at the property level,
|
|
// and is a convenient override to pass down to this method.
|
|
//
|
|
// The following are written into, these are expected to be fresh StringBuilders:
|
|
// @args: arguments that should be passed to native
|
|
// @convs: conversions to perform before the invocation
|
|
// @disposes: dispose operations to perform after the invocation
|
|
// @by_ref_processing
|
|
void GenerateTypeLowering (MethodInfo mi, bool null_allowed_override, out StringBuilder args, out StringBuilder convs, out StringBuilder disposes, out StringBuilder by_ref_processing, out StringBuilder by_ref_init, PropertyInfo propInfo = null, bool castEnum = true)
|
|
{
|
|
args = new StringBuilder ();
|
|
convs = new StringBuilder ();
|
|
disposes = new StringBuilder ();
|
|
by_ref_processing = new StringBuilder();
|
|
by_ref_init = new StringBuilder ();
|
|
|
|
foreach (var pi in mi.GetParameters ()){
|
|
MarshalInfo mai = new MarshalInfo (this, mi, pi);
|
|
|
|
if (!IsTarget (pi)){
|
|
// Construct invocation
|
|
args.Append (", ");
|
|
args.Append (MarshalParameter (mi, pi, null_allowed_override, propInfo, castEnum));
|
|
|
|
if (pi.ParameterType.IsByRef) {
|
|
var et = pi.ParameterType.GetElementType ();
|
|
var nullable = TypeManager.GetUnderlyingNullableType (et);
|
|
if (nullable != null) {
|
|
convs.Append ("var converted = IntPtr.Zero;\n");
|
|
convs.Append ($"var v = default ({FormatType (mi.DeclaringType, nullable)});\n");
|
|
convs.Append ("if (value.HasValue) {\n");
|
|
convs.Append ("\tv = value.Value;\n");
|
|
convs.Append ("\tconverted = new IntPtr (&v);\n");
|
|
convs.Append ("}\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Construct conversions
|
|
if (mai.Type == TypeManager.System_String && !mai.PlainString){
|
|
bool probe_null = null_allowed_override || AttributeManager.HasAttribute<NullAllowedAttribute> (pi);
|
|
|
|
convs.AppendFormat (GenerateMarshalString (probe_null, !mai.ZeroCopyStringMarshal), pi.Name, pi.Name.GetSafeParamName ());
|
|
disposes.AppendFormat (GenerateDisposeString (probe_null, !mai.ZeroCopyStringMarshal), pi.Name);
|
|
} else if (mai.Type.IsArray){
|
|
Type etype = mai.Type.GetElementType ();
|
|
if (HasBindAsAttribute (pi)) {
|
|
convs.AppendFormat ("var nsb_{0} = {1}\n", pi.Name, GetToBindAsWrapper (mi, null, pi));
|
|
disposes.AppendFormat ("\nnsb_{0}?.Dispose ();", pi.Name);
|
|
} else if (HasBindAsAttribute (propInfo)) {
|
|
disposes.AppendFormat ("\nnsb_{0}?.Dispose ();", propInfo.Name);
|
|
} else if (etype == TypeManager.System_String) {
|
|
if (null_allowed_override || AttributeManager.HasAttribute<NullAllowedAttribute> (pi)) {
|
|
convs.AppendFormat ("var nsa_{0} = {1} is null ? null : NSArray.FromStrings ({1});\n", pi.Name, pi.Name.GetSafeParamName ());
|
|
disposes.AppendFormat ("if (nsa_{0} != null)\n\tnsa_{0}.Dispose ();\n", pi.Name);
|
|
} else {
|
|
convs.AppendFormat ("var nsa_{0} = NSArray.FromStrings ({1});\n", pi.Name, pi.Name.GetSafeParamName ());
|
|
disposes.AppendFormat ("nsa_{0}.Dispose ();\n", pi.Name);
|
|
}
|
|
} else if (etype == TypeManager.Selector) {
|
|
exceptions.Add (ErrorHelper.CreateError (1065, mai.Type.FullName, string.IsNullOrEmpty (pi.Name) ? $"#{pi.Position}" : pi.Name, mi.DeclaringType.FullName, mi.Name));
|
|
} else {
|
|
if (null_allowed_override || AttributeManager.HasAttribute<NullAllowedAttribute> (pi)) {
|
|
convs.AppendFormat ("var nsa_{0} = {1} is null ? null : NSArray.FromNSObjects ({1});\n", pi.Name, pi.Name.GetSafeParamName ());
|
|
disposes.AppendFormat ("if (nsa_{0} != null)\n\tnsa_{0}.Dispose ();\n", pi.Name);
|
|
} else {
|
|
convs.AppendFormat ("var nsa_{0} = NSArray.FromNSObjects ({1});\n", pi.Name, pi.Name.GetSafeParamName ());
|
|
disposes.AppendFormat ("nsa_{0}.Dispose ();\n", pi.Name);
|
|
}
|
|
}
|
|
} else if (mai.Type.IsSubclassOf (TypeManager.System_Delegate)){
|
|
string trampoline_name = MakeTrampoline (pi.ParameterType).StaticName;
|
|
string extra = "";
|
|
bool null_allowed = AttributeManager.HasAttribute<NullAllowedAttribute> (pi);
|
|
|
|
convs.AppendFormat ("BlockLiteral *block_ptr_{0};\n", pi.Name);
|
|
convs.AppendFormat ("BlockLiteral block_{0};\n", pi.Name);
|
|
if (null_allowed){
|
|
convs.AppendFormat ("if ({0} is null){{\n", pi.Name.GetSafeParamName ());
|
|
convs.AppendFormat ("\tblock_ptr_{0} = null;\n", pi.Name);
|
|
convs.AppendFormat ("}} else {{\n");
|
|
extra = "\t";
|
|
}
|
|
convs.AppendFormat (extra + "block_{0} = new BlockLiteral ();\n", pi.Name);
|
|
convs.AppendFormat (extra + "block_ptr_{0} = &block_{0};\n", pi.Name);
|
|
convs.AppendFormat (extra + "block_{0}.SetupBlockUnsafe (Trampolines.{1}.Handler, {2});\n", pi.Name, trampoline_name, pi.Name.GetSafeParamName ());
|
|
if (null_allowed)
|
|
convs.AppendFormat ("}}\n");
|
|
|
|
if (null_allowed){
|
|
disposes.AppendFormat ("if (block_ptr_{0} != null)\n", pi.Name);
|
|
}
|
|
disposes.AppendFormat (extra + "block_ptr_{0}->CleanupBlock ();\n", pi.Name);
|
|
} else if (pi.ParameterType.IsGenericParameter) {
|
|
// convs.AppendFormat ("{0}.Handle", pi.Name.GetSafeParamName ());
|
|
} else if (HasBindAsAttribute (pi)) {
|
|
convs.AppendFormat ("var nsb_{0} = {1}\n", pi.Name, GetToBindAsWrapper (mi, null, pi));
|
|
} else {
|
|
if (mai.Type.IsClass && !mai.Type.IsByRef &&
|
|
(mai.Type != TypeManager.Selector && mai.Type != TypeManager.Class && mai.Type != TypeManager.System_String && !TypeManager.INativeObject.IsAssignableFrom (mai.Type)))
|
|
throw new BindingException (1020, true, mai.Type, mi.DeclaringType, mi.Name, mai.Type.IsByRef);
|
|
}
|
|
|
|
// Handle ByRef
|
|
if (mai.Type.IsByRef && mai.Type.GetElementType ().IsValueType == false){
|
|
// For out/ref parameters we support:
|
|
// * string and string[]
|
|
// * NSObject (and subclasses), and array of NSObject (and subclasses)
|
|
// * INativeObject subclasses (but not INativeObject itself), and array of INativeObject subclasses (but not arrays of INativeObject itself)
|
|
// * Except that we do not support arrays of Selector
|
|
// Modifications to an array (i.e. changing an element) are not marshalled back.
|
|
// If the array is modified, then a new array instance must be created and assigned to the ref/out parameter for us to marshal back any modifications.
|
|
var elementType = mai.Type.GetElementType ();
|
|
var isString = elementType == TypeManager.System_String;
|
|
var isINativeObject = elementType == TypeManager.INativeObject;
|
|
var isINativeObjectSubclass = !isINativeObject && TypeManager.INativeObject.IsAssignableFrom (elementType);
|
|
var isNSObject = IsNSObject (elementType);
|
|
var isForcedType = HasForcedAttribute (pi, out var isForcedOwns);
|
|
var isArray = elementType.IsArray;
|
|
var isArrayOfString = isArray && elementType.GetElementType () == TypeManager.System_String;
|
|
var isArrayOfNSObject = isArray && IsNSObject (elementType.GetElementType ());
|
|
var isArrayOfINativeObject = isArray && elementType.GetElementType () == TypeManager.INativeObject;
|
|
var isArrayOfINativeObjectSubclass = isArray && TypeManager.INativeObject.IsAssignableFrom (elementType.GetElementType ());
|
|
var isArrayOfSelector = isArray && elementType.GetElementType () == TypeManager.Selector;
|
|
|
|
if (!isString && !isArrayOfNSObject && !isNSObject && !isArrayOfString && !isINativeObjectSubclass && !isArrayOfINativeObjectSubclass || isINativeObject || isArrayOfSelector || isArrayOfINativeObject) {
|
|
exceptions.Add (ErrorHelper.CreateError (1064, elementType.FullName, string.IsNullOrEmpty (pi.Name) ? $"#{pi.Position}" : pi.Name, mi.DeclaringType.FullName, mi.Name));
|
|
continue;
|
|
}
|
|
|
|
if (pi.IsOut) {
|
|
by_ref_init.AppendFormat ("{1} {0}Value = IntPtr.Zero;\n", pi.Name.GetSafeParamName (), NativeHandleType);
|
|
} else {
|
|
by_ref_init.AppendFormat ("{1} {0}Value = ", pi.Name.GetSafeParamName (), NativeHandleType);
|
|
if (isString) {
|
|
by_ref_init.AppendFormat ("NSString.CreateNative ({0}, true);\n", pi.Name.GetSafeParamName ());
|
|
by_ref_init.AppendFormat ("{1} {0}OriginalValue = {0}Value;\n", pi.Name.GetSafeParamName (), NativeHandleType);
|
|
} else if (isArrayOfNSObject || isArrayOfINativeObjectSubclass) {
|
|
by_ref_init.Insert (0, string.Format ("NSArray {0}ArrayValue = NSArray.FromNSObjects ({0});\n", pi.Name.GetSafeParamName ()));
|
|
by_ref_init.AppendFormat ("{0}ArrayValue is null ? NativeHandle.Zero : {0}ArrayValue.Handle;\n", pi.Name.GetSafeParamName ());
|
|
} else if (isArrayOfString) {
|
|
by_ref_init.Insert (0, string.Format ("NSArray {0}ArrayValue = {0} is null ? null : NSArray.FromStrings ({0});\n", pi.Name.GetSafeParamName ()));
|
|
by_ref_init.AppendFormat ("{0}ArrayValue is null ? NativeHandle.Zero : {0}ArrayValue.Handle;\n", pi.Name.GetSafeParamName ());
|
|
} else if (isNSObject || isINativeObjectSubclass) {
|
|
by_ref_init.AppendFormat ("{0} is null ? NativeHandle.Zero : {0}.Handle;\n", pi.Name.GetSafeParamName ());
|
|
} else {
|
|
throw ErrorHelper.CreateError (88, mai.Type, mi);
|
|
}
|
|
}
|
|
|
|
if (isString) {
|
|
if (!pi.IsOut)
|
|
by_ref_processing.AppendFormat ("if ({0}Value != {0}OriginalValue)\n\t", pi.Name.GetSafeParamName ());
|
|
by_ref_processing.AppendFormat ("{0} = CFString.FromHandle ({0}Value)!;\n", pi.Name.GetSafeParamName ());
|
|
} else if (isArray) {
|
|
if (!pi.IsOut)
|
|
by_ref_processing.AppendFormat ("if ({0}Value != ({0}ArrayValue is null ? NativeHandle.Zero : {0}ArrayValue.Handle))\n\t", pi.Name.GetSafeParamName ());
|
|
|
|
if (isArrayOfNSObject || isArrayOfINativeObjectSubclass) {
|
|
by_ref_processing.AppendFormat ("{0} = CFArray.ArrayFromHandle<{1}> ({0}Value)!;\n", pi.Name.GetSafeParamName (), RenderType (elementType.GetElementType ()));
|
|
} else if (isArrayOfString) {
|
|
by_ref_processing.AppendFormat ("{0} = CFArray.StringArrayFromHandle ({0}Value)!;\n", pi.Name.GetSafeParamName ());
|
|
} else {
|
|
throw ErrorHelper.CreateError (88, mai.Type, mi);
|
|
}
|
|
|
|
if (!pi.IsOut)
|
|
by_ref_processing.AppendFormat ("{0}ArrayValue?.Dispose ();\n", pi.Name.GetSafeParamName ());
|
|
} else if (isNSObject && !isForcedType) {
|
|
by_ref_processing.AppendFormat ("{0} = Runtime.GetNSObject<{1}> ({0}Value)!;\n", pi.Name.GetSafeParamName (), RenderType (elementType));
|
|
} else if (isINativeObjectSubclass) {
|
|
if (!pi.IsOut)
|
|
by_ref_processing.AppendFormat ("if ({0}Value != ({0} is null ? NativeHandle.Zero : {0}.Handle))\n\t", pi.Name.GetSafeParamName ());
|
|
by_ref_processing.AppendFormat ("{0} = Runtime.GetINativeObject<{1}> ({0}Value, {2}, {3})!;\n", pi.Name.GetSafeParamName (), RenderType (elementType), isForcedType ? "true" : "false", isForcedType ? isForcedOwns : "false");
|
|
} else {
|
|
throw ErrorHelper.CreateError (88, mai.Type, mi);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void GenerateArgumentChecks (MethodInfo mi, bool null_allowed_override, PropertyInfo propInfo = null)
|
|
{
|
|
if (AttributeManager.HasAttribute<NullAllowedAttribute> (mi))
|
|
ErrorHelper.Show (new BindingException (1118, false, mi));
|
|
|
|
foreach (var pi in mi.GetParameters ()) {
|
|
var safe_name = pi.Name.GetSafeParamName ();
|
|
bool protocolize = Protocolize (pi);
|
|
if (!BindThirdPartyLibrary) {
|
|
if (!mi.IsSpecialName && IsModel (pi.ParameterType) && !protocolize) {
|
|
// don't warn on obsoleted API, there's likely a new version that fix this
|
|
// any no good reason for using the obsolete API anyway
|
|
if (!AttributeManager.HasAttribute <ObsoleteAttribute> (mi) && !AttributeManager.HasAttribute<ObsoleteAttribute> (mi.DeclaringType))
|
|
ErrorHelper.Warning (1106, mi.DeclaringType, mi.Name, pi.Name, pi.ParameterType, pi.ParameterType.Namespace, pi.ParameterType.Name);
|
|
}
|
|
}
|
|
|
|
var needs_null_check = ParameterNeedsNullCheck (pi, mi, propInfo);
|
|
if (protocolize) {
|
|
print ("if ({0} != null) {{", safe_name);
|
|
print ("\tif (!({0} is NSObject))\n", safe_name);
|
|
print ("\t\tthrow new ArgumentException (\"The object passed of type \" + {0}.GetType () + \" does not derive from NSObject\");", safe_name);
|
|
print ("}");
|
|
}
|
|
|
|
var cap = propInfo?.SetMethod == mi ? (ICustomAttributeProvider) propInfo : (ICustomAttributeProvider) pi;
|
|
var bind_as = GetBindAsAttribute (cap);
|
|
var pit = bind_as == null ? pi.ParameterType : bind_as.Type;
|
|
if (IsWrappedType (pit) || TypeManager.INativeObject.IsAssignableFrom (pit)) {
|
|
if (needs_null_check && !null_allowed_override) {
|
|
print ($"var {safe_name}__handle__ = {safe_name}!.GetNonNullHandle (nameof ({safe_name}));");
|
|
} else {
|
|
print ($"var {safe_name}__handle__ = {safe_name}.GetHandle ();");
|
|
}
|
|
} else if (needs_null_check) {
|
|
print ("if ({0} is null)", safe_name);
|
|
print ("\tObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof ({0}));", safe_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
// undecorated code is assumed to be iOS 2.0
|
|
static AvailabilityBaseAttribute iOSIntroducedDefault = new IntroducedAttribute (PlatformName.iOS, 2, 0);
|
|
|
|
string CurrentMethod;
|
|
|
|
void GenerateThreadCheck (StreamWriter sw = null)
|
|
{
|
|
string s;
|
|
switch (CurrentPlatform) {
|
|
case PlatformName.iOS:
|
|
case PlatformName.WatchOS:
|
|
case PlatformName.TvOS:
|
|
case PlatformName.MacCatalyst:
|
|
s = $"global::UIKit.UIApplication.EnsureUIThread ();";
|
|
break;
|
|
case PlatformName.MacOSX:
|
|
s = $"global::AppKit.NSApplication.EnsureUIThread ();";
|
|
break;
|
|
default:
|
|
throw new BindingException (1047, CurrentPlatform);
|
|
}
|
|
if (sw == null)
|
|
print (s);
|
|
else
|
|
sw.WriteLine (s);
|
|
}
|
|
|
|
// Stret.NeedStret is shared between generator and X.I dll so in order to wrap the exception
|
|
// into a BindingException we need to set the try/catch here so we can provide a better message. Bugzilla ref 51212.
|
|
bool CheckNeedStret (MethodInfo mi)
|
|
{
|
|
try {
|
|
return Stret.NeedStret (mi.ReturnType, this);
|
|
}
|
|
catch (TypeLoadException ex) {
|
|
throw new BindingException (0001, true, mi.ReturnType.Name, ex.Message);
|
|
}
|
|
}
|
|
|
|
//
|
|
// The NullAllowed can be applied on a property, to avoid the ugly syntax, we allow it on the property
|
|
// So we need to pass this as `null_allowed_override', This should only be used by setters.
|
|
//
|
|
public void GenerateMethodBody (MemberInformation minfo, MethodInfo mi, string sel, bool null_allowed_override, string var_name, BodyOption body_options, PropertyInfo propInfo = null)
|
|
{
|
|
var type = minfo.type;
|
|
var category_type = minfo.category_extension_type;
|
|
var is_appearance = minfo.is_appearance;
|
|
TrampolineInfo trampoline_info = null;
|
|
|
|
CurrentMethod = String.Format ("{0}.{1}", type.Name, mi.Name);
|
|
|
|
// Warn about [Static] used in a member of [Category]
|
|
var hasStaticAtt = AttributeManager.HasAttribute<StaticAttribute> (mi);
|
|
if (category_type != null && hasStaticAtt && !minfo.ignore_category_static_warnings) {
|
|
var baseTypeAtt = AttributeManager.GetCustomAttribute<BaseTypeAttribute> (minfo.type);
|
|
ErrorHelper.Warning (1117, mi.Name, type.FullName, baseTypeAtt?.BaseType.FullName);
|
|
}
|
|
|
|
indent++;
|
|
// if the namespace/type needs it and if the member is NOT marked as safe (don't check)
|
|
// if the namespace/type does NOT need it and if the member is marked as NOT safe (do check)
|
|
if (type_needs_thread_checks ? (minfo.threadCheck != ThreadCheck.Off) : (minfo.threadCheck == ThreadCheck.On))
|
|
GenerateThreadCheck ();
|
|
|
|
Inject<PrologueSnippetAttribute> (mi);
|
|
|
|
GenerateArgumentChecks (mi, false, propInfo);
|
|
|
|
// Collect all strings that can be fast-marshalled
|
|
List<string> stringParameters = CollectFastStringMarshalParameters (mi);
|
|
|
|
GenerateTypeLowering (mi, null_allowed_override, out var args, out var convs, out var disposes, out var by_ref_processing, out var by_ref_init, propInfo);
|
|
|
|
var argsArray = args.ToString ();
|
|
|
|
if (by_ref_init.Length > 0)
|
|
print (by_ref_init.ToString ());
|
|
|
|
if (stringParameters != null){
|
|
print ("fixed (char * {0}){{",
|
|
stringParameters.Select (name => "_p" + name + " = " + name).Aggregate ((first,second) => first + ", " + second));
|
|
indent++;
|
|
}
|
|
|
|
if (propInfo != null && IsSetter (mi) && HasBindAsAttribute (propInfo)) {
|
|
convs.AppendFormat ("var nsb_{0} = {1}\n", propInfo.Name, GetToBindAsWrapper (mi, minfo, null));
|
|
}
|
|
|
|
if (convs.Length > 0)
|
|
print (sw, convs.ToString ());
|
|
|
|
Inject<PreSnippetAttribute> (mi);
|
|
var align = AttributeManager.GetCustomAttribute<AlignAttribute> (mi);
|
|
|
|
PostGetAttribute [] postget = null;
|
|
// [PostGet] are not needed (and might not be available) when generating methods inside Appearance types
|
|
// However we want them even if ImplementsAppearance is true (i.e. the original type needs them)
|
|
if (!is_appearance) {
|
|
postget = AttributeManager.GetCustomAttributes<PostGetAttribute> (mi);
|
|
if (postget.Length == 0 && propInfo != null)
|
|
postget = AttributeManager.GetCustomAttributes<PostGetAttribute> (propInfo);
|
|
|
|
if (postget != null && postget.Length == 0)
|
|
postget = null;
|
|
}
|
|
|
|
var shouldMarshalNativeExceptions = ShouldMarshalNativeExceptions (mi);
|
|
if (shouldMarshalNativeExceptions)
|
|
print ("IntPtr exception_gchandle = IntPtr.Zero;");
|
|
|
|
bool use_temp_return =
|
|
minfo.is_return_release ||
|
|
(mi.Name != "Constructor" && shouldMarshalNativeExceptions && mi.ReturnType != TypeManager.System_Void) ||
|
|
(mi.Name != "Constructor" && (CheckNeedStret (mi) || disposes.Length > 0 || postget != null) && mi.ReturnType != TypeManager.System_Void) ||
|
|
(AttributeManager.HasAttribute<FactoryAttribute> (mi)) ||
|
|
((body_options & BodyOption.NeedsTempReturn) == BodyOption.NeedsTempReturn) ||
|
|
(mi.ReturnType.IsSubclassOf (TypeManager.System_Delegate)) ||
|
|
(AttributeManager.HasAttribute<ProxyAttribute> (AttributeManager.GetReturnTypeCustomAttributes (mi))) ||
|
|
(IsNativeEnum (mi.ReturnType)) ||
|
|
(mi.Name != "Constructor" && by_ref_processing.Length > 0 && mi.ReturnType != TypeManager.System_Void);
|
|
|
|
if (use_temp_return) {
|
|
// for properties we (most often) put the attribute on the property itself, not the getter/setter methods
|
|
if (mi.ReturnType.IsSubclassOf (TypeManager.System_Delegate)) {
|
|
print ("{0} ret;", NativeHandleType);
|
|
trampoline_info = MakeTrampoline (mi.ReturnType);
|
|
} else if (align != null) {
|
|
print ("{0} ret = default({0});", FormatType (mi.DeclaringType, mi.ReturnType));
|
|
print ("IntPtr ret_alloced = Marshal.AllocHGlobal (Marshal.SizeOf (typeof ({0})) + {1});", FormatType (mi.DeclaringType, mi.ReturnType), align.Align);
|
|
print ("IntPtr aligned_ret = new IntPtr (((nint) (ret_alloced + {0}) >> {1}) << {1});", align.Align - 1, align.Bits);
|
|
print ("bool aligned_assigned = false;");
|
|
} else if (minfo.protocolize) {
|
|
print ("{0} ret;", FormatType (mi.DeclaringType, mi.ReturnType.Namespace, FindProtocolInterface (mi.ReturnType, mi)));
|
|
} else if (minfo.is_bindAs) {
|
|
var bindAsAttrib = GetBindAsAttribute (minfo.mi);
|
|
// tricky, e.g. when an nullable `NSNumber[]` is bound as a `float[]`, since FormatType and bindAsAttrib have not clue about the original nullability
|
|
print ("{0} ret;", FormatType (bindAsAttrib.Type.DeclaringType, GetCorrectGenericType (bindAsAttrib.Type)));
|
|
} else {
|
|
var isClassType = mi.ReturnType.IsClass || mi.ReturnType.IsInterface;
|
|
var nullableReturn = isClassType ? "?" : string.Empty;
|
|
print ("{0}{1} ret;", FormatType (mi.DeclaringType, GetCorrectGenericType (mi.ReturnType)), nullableReturn);
|
|
}
|
|
}
|
|
|
|
bool needs_temp = use_temp_return || disposes.Length > 0;
|
|
if (minfo.is_virtual_method || mi.Name == "Constructor"){
|
|
//print ("if (this.GetType () == TypeManager.{0}) {{", type.Name);
|
|
if (external || minfo.is_interface_impl || minfo.is_extension_method) {
|
|
GenerateNewStyleInvoke (false, mi, minfo, sel, argsArray, needs_temp, category_type);
|
|
} else {
|
|
var may_throw = shouldMarshalNativeExceptions;
|
|
var null_handle = may_throw && mi.Name == "Constructor";
|
|
if (null_handle) {
|
|
print ("try {");
|
|
indent++;
|
|
}
|
|
|
|
WriteIsDirectBindingCondition (sw, ref indent, is_direct_binding,
|
|
mi.Name == "Constructor" ? is_direct_binding_value : null, // We only need to print the is_direct_binding value in constructors
|
|
() => { GenerateNewStyleInvoke (false, mi, minfo, sel, argsArray, needs_temp, category_type); return null; },
|
|
() => { GenerateNewStyleInvoke (true, mi, minfo, sel, argsArray, needs_temp, category_type); return null; }
|
|
);
|
|
|
|
if (null_handle) {
|
|
indent--;
|
|
print ("} catch {");
|
|
indent++;
|
|
print ("Handle = IntPtr.Zero;");
|
|
print ("throw;");
|
|
indent--;
|
|
print ("}");
|
|
}
|
|
}
|
|
} else {
|
|
GenerateNewStyleInvoke (false, mi, minfo, sel, argsArray, needs_temp, category_type);
|
|
}
|
|
|
|
if (shouldMarshalNativeExceptions)
|
|
print ("Runtime.ThrowException (exception_gchandle);");
|
|
|
|
if (minfo.is_return_release) {
|
|
|
|
// Make sure we generate the required signature in Messaging only if needed
|
|
// bool_objc_msgSendSuper_IntPtr: for respondsToSelector:
|
|
if (!send_methods.ContainsKey ("void_objc_msgSend")) {
|
|
print (m, "[DllImport (LIBOBJC_DYLIB, EntryPoint=\"objc_msgSendSuper\")]");
|
|
print (m, "public extern static void void_objc_msgSend (IntPtr receiever, IntPtr selector);");
|
|
RegisterMethodName ("void_objc_msgSend");
|
|
}
|
|
|
|
print ("if (ret != null)");
|
|
indent++;
|
|
print ("global::{0}.void_objc_msgSend (ret.Handle, Selector.GetHandle (\"release\"));", ns.Messaging);
|
|
indent--;
|
|
}
|
|
|
|
Inject<PostSnippetAttribute> (mi);
|
|
|
|
if (disposes.Length > 0)
|
|
print (sw, disposes.ToString ());
|
|
if ((body_options & BodyOption.StoreRet) == BodyOption.StoreRet) {
|
|
// nothing to do
|
|
} else if ((body_options & BodyOption.CondStoreRet) == BodyOption.CondStoreRet) {
|
|
// nothing to do
|
|
} else if ((body_options & BodyOption.MarkRetDirty) == BodyOption.MarkRetDirty) {
|
|
print ("MarkDirty ();");
|
|
print ("{0} = ret;", var_name);
|
|
}
|
|
|
|
if ((postget != null) && (postget.Length > 0)) {
|
|
print ("#pragma warning disable 168");
|
|
for (int i = 0; i < postget.Length; i++) {
|
|
if (IsDisableForNewRefCount (postget [i], type))
|
|
continue;
|
|
|
|
|
|
bool version_check = false;
|
|
if (CurrentPlatform != PlatformName.MacOSX) {
|
|
// bug #7742: if this code, e.g. existing in iOS 2.0,
|
|
// tries to call a property available since iOS 5.0,
|
|
// then it will fail when executing in iOS 4.3
|
|
var postget_avail = GetIntroduced (type, postget [i].MethodName);
|
|
if (postget_avail != null) {
|
|
var caller_avail = GetIntroduced (mi, propInfo) ?? iOSIntroducedDefault;
|
|
if (caller_avail.Version < postget_avail.Version) {
|
|
version_check = true;
|
|
print ("var postget{0} = {4}.UIDevice.CurrentDevice.CheckSystemVersion ({1},{2}) ? {3} : null;",
|
|
i,
|
|
postget_avail.Version.Major,
|
|
postget_avail.Version.Minor,
|
|
postget [i].MethodName,
|
|
"UIKit");
|
|
}
|
|
}
|
|
}
|
|
if (!version_check)
|
|
print ("var postget{0} = {1};", i, postget [i].MethodName);
|
|
}
|
|
print ("#pragma warning restore 168");
|
|
}
|
|
|
|
if (AttributeManager.HasAttribute<FactoryAttribute> (mi))
|
|
print ("ret.Release (); // Release implicit ref taken by GetNSObject");
|
|
if (by_ref_processing.Length > 0)
|
|
print (sw, by_ref_processing.ToString ());
|
|
if (use_temp_return) {
|
|
if (AttributeManager.HasAttribute<ProxyAttribute> (AttributeManager.GetReturnTypeCustomAttributes (mi)))
|
|
print ("ret.IsDirectBinding = true;");
|
|
|
|
if (mi.ReturnType.IsSubclassOf (TypeManager.System_Delegate)) {
|
|
print ("return global::ObjCRuntime.Trampolines.{0}.Create (ret)!;", trampoline_info.NativeInvokerName);
|
|
} else if (align != null) {
|
|
print ("if (aligned_assigned)");
|
|
indent++;
|
|
print ("unsafe {{ ret = *({0} *) aligned_ret; }}", FormatType (mi.DeclaringType, mi.ReturnType));
|
|
indent--;
|
|
print ("Marshal.FreeHGlobal (ret_alloced);");
|
|
print ("return ret;");
|
|
} else {
|
|
// we can't be 100% confident that the ObjC API annotations are correct so we always null check inside generated code
|
|
print ("return ret!;");
|
|
}
|
|
}
|
|
if (minfo.is_ctor)
|
|
WriteMarkDirtyIfDerived (sw, mi.DeclaringType);
|
|
if (stringParameters != null){
|
|
indent--;
|
|
print ("}");
|
|
}
|
|
indent--;
|
|
}
|
|
|
|
|
|
PropertyInfo GetProperty (PostGetAttribute @this, Type type)
|
|
{
|
|
if (type == null || type == TypeManager.System_Object)
|
|
return null;
|
|
|
|
var props = type.GetProperties ();
|
|
foreach (var pi in props) {
|
|
if (pi.Name != @this.MethodName)
|
|
continue;
|
|
return pi;
|
|
}
|
|
return GetProperty (@this, ReflectionExtensions.GetBaseType (type, this));
|
|
}
|
|
|
|
bool IsDisableForNewRefCount (PostGetAttribute @this, Type type)
|
|
{
|
|
PropertyInfo p = GetProperty (@this, type);
|
|
var ea = AttributeManager.GetCustomAttributes<ExportAttribute> (p);
|
|
var sem = ea [0].ArgumentSemantic;
|
|
return (sem != ArgumentSemantic.Assign && sem != ArgumentSemantic.Weak); // also cover UnsafeUnretained
|
|
}
|
|
|
|
public IEnumerable<MethodInfo> GetTypeContractMethods (Type source)
|
|
{
|
|
if (source.IsEnum)
|
|
yield break;
|
|
foreach (var method in source.GatherMethods (BindingFlags.Public | BindingFlags.Instance, this))
|
|
yield return method;
|
|
foreach (var parent in source.GetInterfaces ()){
|
|
// skip interfaces that aren't available on the current platform
|
|
if (parent.IsUnavailable (this))
|
|
continue;
|
|
// skip case where the interface implemented comes from an already built assembly (e.g. monotouch.dll)
|
|
// e.g. Dispose won't have an [Export] since it's present to satisfy System.IDisposable
|
|
if (parent.FullName != "System.IDisposable") {
|
|
foreach (var method in parent.GatherMethods (BindingFlags.Public | BindingFlags.Instance, this)) {
|
|
yield return method;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public IEnumerable<PropertyInfo> GetTypeContractProperties (Type source)
|
|
{
|
|
foreach (var prop in source.GatherProperties (this))
|
|
yield return prop;
|
|
foreach (var parent in source.GetInterfaces ()){
|
|
// skip interfaces that aren't available on the current platform
|
|
if (parent.IsUnavailable (this))
|
|
continue;
|
|
// skip case where the interface implemented comes from an already built assembly (e.g. monotouch.dll)
|
|
// e.g. the Handle property won't have an [Export] since it's present to satisfyINativeObject
|
|
if (parent.Name != "INativeObject") {
|
|
foreach (var prop in parent.GatherProperties (this))
|
|
yield return prop;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// This is used to determine if the memberType is in the declaring type or in any of the
|
|
// inherited versions of the type. We use this now, since we support inlining protocols
|
|
//
|
|
public bool MemberBelongsToType (Type memberType, Type hostType)
|
|
{
|
|
if (memberType == hostType)
|
|
return true;
|
|
// we also need to inline the base type, e.g. UITableViewDelegate must bring UIScrollViewDelegate
|
|
if (MemberBelongToInterface (memberType, hostType)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool MemberBelongToInterface (Type memberType, Type intf)
|
|
{
|
|
if (memberType == intf)
|
|
return true;
|
|
foreach (var p in intf.GetInterfaces ()) {
|
|
if (memberType == p)
|
|
return true;
|
|
if (MemberBelongToInterface (memberType, ReflectionExtensions.GetBaseType (p, this)))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Dictionary<string,object> generatedEvents = new Dictionary<string,object> ();
|
|
Dictionary<string,object> generatedDelegates = new Dictionary<string,object> ();
|
|
|
|
bool DoesTypeNeedBackingField (Type type) {
|
|
return IsWrappedType (type) || (type.IsArray && IsWrappedType (type.GetElementType ()));
|
|
}
|
|
|
|
bool DoesPropertyNeedBackingField (PropertyInfo pi) {
|
|
return DoesTypeNeedBackingField (pi.PropertyType) && !AttributeManager.HasAttribute<TransientAttribute> (pi);
|
|
}
|
|
|
|
bool DoesPropertyNeedDirtyCheck (PropertyInfo pi, ExportAttribute ea)
|
|
{
|
|
switch (ea?.ArgumentSemantic) {
|
|
case ArgumentSemantic.Copy:
|
|
case ArgumentSemantic.Retain: // same as Strong
|
|
case ArgumentSemantic.None:
|
|
return DoesPropertyNeedBackingField (pi);
|
|
default: // Assign (same as UnsafeUnretained) or Weak
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void PrintPropertyAttributes (PropertyInfo pi, Type type, bool skipTypeInjection = false)
|
|
{
|
|
foreach (var oa in AttributeManager.GetCustomAttributes<ObsoleteAttribute> (pi)) {
|
|
print ("[Obsolete (\"{0}\", {1})]", oa.Message, oa.IsError ? "true" : "false");
|
|
print ("[EditorBrowsable (EditorBrowsableState.Never)]");
|
|
}
|
|
|
|
foreach (var ba in AttributeManager.GetCustomAttributes<DebuggerBrowsableAttribute> (pi))
|
|
print ("[DebuggerBrowsable (DebuggerBrowsableState.{0})]", ba.State);
|
|
|
|
foreach (var da in AttributeManager.GetCustomAttributes<DebuggerDisplayAttribute> (pi)) {
|
|
var narg = da.Name != null ? string.Format (", Name = \"{0}\"", da.Name) : string.Empty;
|
|
var targ = da.Type != null ? string.Format (", Type = \"{0}\"", da.Type) : string.Empty;
|
|
print ("[DebuggerDisplay (\"{0}\"{1}{2})]", da.Value, narg, targ);
|
|
}
|
|
foreach (var oa in AttributeManager.GetCustomAttributes<OptionalImplementationAttribute> (pi)) {
|
|
print ("[DebuggerBrowsable (DebuggerBrowsableState.Never)]");
|
|
}
|
|
|
|
// if we inline properties (e.g. from a protocol)
|
|
// we must look if the type has an [Availability] attribute
|
|
if (type != pi.DeclaringType) {
|
|
// print, if not duplicated from the type (being inlined into), the property availability
|
|
if (!PrintPlatformAttributes (pi, type) && !skipTypeInjection) {
|
|
// print, if not duplicated from the type (being inlined into), the property declaring type (protocol) availability
|
|
PrintPlatformAttributes (pi.DeclaringType, type);
|
|
}
|
|
} else {
|
|
PrintPlatformAttributes (pi, type);
|
|
}
|
|
|
|
foreach (var sa in AttributeManager.GetCustomAttributes<ThreadSafeAttribute> (pi))
|
|
print (sa.Safe ? "[ThreadSafe]" : "[ThreadSafe (false)]");
|
|
}
|
|
|
|
void GenerateProperty (Type type, PropertyInfo pi, List<string> instance_fields_to_clear_on_dispose, bool is_model, bool is_interface_impl = false)
|
|
{
|
|
string wrap;
|
|
var export = GetExportAttribute (pi, out wrap);
|
|
var minfo = new MemberInformation (this, this, pi, type, is_interface_impl);
|
|
bool use_underscore = minfo.is_unified_internal;
|
|
var mod = minfo.GetVisibility ();
|
|
minfo.protocolize = Protocolize (pi);
|
|
|
|
var nullable = !pi.PropertyType.IsValueType && AttributeManager.HasAttribute<NullAllowedAttribute> (pi);
|
|
|
|
// So we don't hide the get or set of a parent property with the same name, we need to see if we have a parent declaring the same property
|
|
PropertyInfo parentBaseType = GetParentTypeWithSameNamedProperty (ReflectionExtensions.GetBaseTypeAttribute (type, this), pi.Name);
|
|
|
|
// If so, we're not static, and we can't both read and write, but they can
|
|
if (!minfo.is_static && !(pi.CanRead && pi.CanWrite) && (parentBaseType != null && parentBaseType.CanRead && parentBaseType.CanWrite)) {
|
|
// Make sure the selector matches, sanity check that we aren't hiding something of a different type
|
|
// We skip this for wrap'ed properties, as those get complicated to resolve the correct export
|
|
if (wrap == null &&
|
|
((pi.CanRead && (GetGetterExportAttribute (pi).Selector != GetGetterExportAttribute (parentBaseType).Selector)) ||
|
|
pi.CanWrite && (GetSetterExportAttribute (pi).Selector != GetSetterExportAttribute (parentBaseType).Selector))) {
|
|
throw new BindingException (1035, true, pi.Name, type, parentBaseType.DeclaringType);
|
|
}
|
|
// Then let's not write out our copy, since we'll reduce visibility
|
|
return;
|
|
}
|
|
|
|
if (!BindThirdPartyLibrary) {
|
|
var elType = pi.PropertyType.IsArray ? pi.PropertyType.GetElementType () : pi.PropertyType;
|
|
|
|
if (IsModel (elType) && !minfo.protocolize) {
|
|
ErrorHelper.Warning (1110, pi.DeclaringType, pi.Name, pi.PropertyType, pi.PropertyType.Namespace, pi.PropertyType.Name);
|
|
}
|
|
}
|
|
|
|
if (wrap != null){
|
|
print_generated_code ();
|
|
PrintPropertyAttributes (pi, minfo.type);
|
|
PrintAttributes (pi, preserve:true, advice:true);
|
|
print ("{0} {1}{2}{3} {4}{5} {{",
|
|
mod,
|
|
minfo.GetModifiers (),
|
|
(minfo.protocolize ? "I" : "") + FormatType (pi.DeclaringType, GetCorrectGenericType (pi.PropertyType)),
|
|
nullable ? "?" : String.Empty,
|
|
pi.Name.GetSafeParamName (),
|
|
use_underscore ? "_" : "");
|
|
indent++;
|
|
if (pi.CanRead) {
|
|
#if !NET
|
|
PrintAttributes (pi, platform:true);
|
|
#endif
|
|
PrintAttributes (pi.GetGetMethod (), platform:true, preserve:true, advice:true);
|
|
print ("get {");
|
|
indent++;
|
|
|
|
if (IsDictionaryContainerType (pi.PropertyType)) {
|
|
print ("var src = {0} != null ? new NSMutableDictionary ({0}) : null;", wrap);
|
|
print ("return src is null ? null! : new {0}(src);", FormatType (pi.DeclaringType, pi.PropertyType));
|
|
} else {
|
|
if (IsArrayOfWrappedType (pi.PropertyType))
|
|
print ("return NSArray.FromArray<{0}>({1} as NSArray);", FormatType (pi.DeclaringType, pi.PropertyType.GetElementType ()), wrap);
|
|
else if (pi.PropertyType.IsValueType)
|
|
print ("return ({0}) ({1});", FormatType (pi.DeclaringType, pi.PropertyType), wrap);
|
|
else
|
|
print ("return ({0} as {1}{2})!;", wrap, minfo.protocolize ? "I" : String.Empty, FormatType (pi.DeclaringType, pi.PropertyType));
|
|
}
|
|
indent--;
|
|
print ("}");
|
|
}
|
|
if (pi.CanWrite) {
|
|
#if !NET
|
|
PrintAttributes (pi, platform:true);
|
|
#endif
|
|
PrintAttributes (pi.GetSetMethod (), platform:true, preserve:true, advice:true);
|
|
print ("set {");
|
|
indent++;
|
|
|
|
var is_protocol_wrapper = IsProtocolInterface (pi.PropertyType, false);
|
|
|
|
if (minfo.protocolize || is_protocol_wrapper){
|
|
print ("var rvalue = value as NSObject;");
|
|
print ("if (!(value is null) && rvalue is null)");
|
|
print ("\tthrow new ArgumentException (\"The object passed of type \" + value.GetType () + \" does not derive from NSObject\");");
|
|
}
|
|
|
|
if (IsDictionaryContainerType (pi.PropertyType))
|
|
print ("{0} = value.GetDictionary ()!;", wrap);
|
|
else {
|
|
if (IsArrayOfWrappedType (pi.PropertyType))
|
|
print ("{0} = NSArray.FromNSObjects (value);", wrap);
|
|
else
|
|
print ("{0} = {1}value;", wrap, minfo.protocolize || is_protocol_wrapper ? "r" : "");
|
|
}
|
|
indent--;
|
|
print ("}");
|
|
}
|
|
|
|
indent--;
|
|
print ("}\n");
|
|
return;
|
|
}
|
|
|
|
string var_name = null;
|
|
|
|
// [Model] has properties that only throws, so there's no point in adding unused backing fields
|
|
if (!is_model && DoesPropertyNeedBackingField (pi) && !is_interface_impl && !minfo.is_static && !DoesPropertyNeedDirtyCheck (pi, export)) {
|
|
var_name = string.Format ("__mt_{0}_var{1}", pi.Name, minfo.is_static ? "_static" : "");
|
|
|
|
print_generated_code ();
|
|
|
|
if (minfo.is_thread_static)
|
|
print ("[ThreadStatic]");
|
|
print ("{1}object? {0};", var_name, minfo.is_static ? "static " : "");
|
|
|
|
if (!minfo.is_static && !is_interface_impl){
|
|
instance_fields_to_clear_on_dispose.Add (var_name);
|
|
}
|
|
}
|
|
|
|
print_generated_code (optimizable: IsOptimizable (pi));
|
|
PrintPropertyAttributes (pi, minfo.type);
|
|
|
|
PrintAttributes (pi, preserve:true, advice:true, bindAs:true);
|
|
|
|
string propertyTypeName;
|
|
if (minfo.protocolize) {
|
|
propertyTypeName = FindProtocolInterface (pi.PropertyType, pi);
|
|
} else if (minfo.is_bindAs) {
|
|
var bindAsAttrib = GetBindAsAttribute (minfo.mi);
|
|
propertyTypeName = FormatType (bindAsAttrib.Type.DeclaringType, GetCorrectGenericType (bindAsAttrib.Type));
|
|
// it remains nullable only if the BindAs type can be null (i.e. a reference type)
|
|
nullable = !bindAsAttrib.Type.IsValueType && AttributeManager.HasAttribute<NullAllowedAttribute> (pi);
|
|
} else {
|
|
propertyTypeName = FormatType (pi.DeclaringType, GetCorrectGenericType (pi.PropertyType));
|
|
}
|
|
|
|
print ("{0} {1}{2}{3} {4}{5} {{",
|
|
mod,
|
|
minfo.GetModifiers (),
|
|
propertyTypeName,
|
|
nullable ? "?" : "",
|
|
pi.Name.GetSafeParamName (),
|
|
use_underscore ? "_" : "");
|
|
indent++;
|
|
|
|
if (minfo.has_inner_wrap_attribute) {
|
|
// If property getter or setter has its own WrapAttribute we let the user do whatever their heart desires
|
|
if (pi.CanRead) {
|
|
PrintAttributes (pi, platform: true);
|
|
PrintAttributes (pi.GetGetMethod (), platform: true, preserve: true, advice: true);
|
|
print ("get {");
|
|
indent++;
|
|
|
|
print ($"return {minfo.wpmi.WrapGetter};");
|
|
|
|
indent--;
|
|
print ("}");
|
|
}
|
|
if (pi.CanWrite) {
|
|
var setter = pi.GetSetMethod ();
|
|
var not_implemented_attr = AttributeManager.GetCustomAttribute<NotImplementedAttribute> (setter);
|
|
|
|
PrintAttributes (pi, platform: true);
|
|
PrintAttributes (setter, platform: true, preserve: true, advice: true, notImplemented: true);
|
|
print ("set {");
|
|
indent++;
|
|
|
|
if (not_implemented_attr != null)
|
|
print ("throw new NotImplementedException ({0});", not_implemented_attr.Message == null ? "" : $@"""{not_implemented_attr.Message}""");
|
|
else
|
|
print ($"{minfo.wpmi.WrapSetter};");
|
|
|
|
indent--;
|
|
print ("}");
|
|
}
|
|
indent--;
|
|
print ("}\n");
|
|
return;
|
|
}
|
|
|
|
if (pi.CanRead){
|
|
var getter = pi.GetGetMethod ();
|
|
var ba = GetBindAttribute (getter);
|
|
string sel = ba != null ? ba.Selector : export.Selector;
|
|
|
|
// print availability separately since we could be inlining
|
|
#if !NET
|
|
PrintPlatformAttributes (pi, type);
|
|
#endif
|
|
PrintAttributes (pi, platform:false);
|
|
|
|
if (!minfo.is_sealed || !minfo.is_wrapper) {
|
|
PrintDelegateProxy (pi.GetGetMethod ());
|
|
PrintExport (minfo, sel, export.ArgumentSemantic);
|
|
}
|
|
|
|
PrintAttributes (pi.GetGetMethod(), platform:true, preserve:true, advice:true, notImplemented:true);
|
|
#if NET
|
|
if (false) {
|
|
#else
|
|
if (minfo.is_abstract){
|
|
print ("get; ");
|
|
#endif
|
|
} else {
|
|
print ("get {");
|
|
var is32BitNotSupported = Is64BitiOSOnly (pi);
|
|
if (is32BitNotSupported) {
|
|
print ("#if ARCH_32");
|
|
print ("\tthrow new PlatformNotSupportedException (\"This API is not supported on this version of iOS\");");
|
|
print ("#else");
|
|
}
|
|
if (debug)
|
|
print ("Console.WriteLine (\"In {0}\");", pi.GetGetMethod ());
|
|
if (is_model)
|
|
print ("\tthrow new ModelNotImplementedException ();");
|
|
else if (minfo.is_abstract)
|
|
print ("throw new You_Should_Not_Call_base_In_This_Method ();");
|
|
else {
|
|
if (minfo.is_autorelease) {
|
|
indent++;
|
|
print ("using (var autorelease_pool = new NSAutoreleasePool ()) {");
|
|
}
|
|
if (is_interface_impl || !DoesPropertyNeedBackingField (pi)) {
|
|
GenerateMethodBody (minfo, getter, sel, false, null, BodyOption.None, pi);
|
|
} else if (minfo.is_static) {
|
|
GenerateMethodBody (minfo, getter, sel, false, var_name, BodyOption.StoreRet, pi);
|
|
} else {
|
|
if (DoesPropertyNeedDirtyCheck (pi, export))
|
|
GenerateMethodBody (minfo, getter, sel, false, var_name, BodyOption.CondStoreRet, pi);
|
|
else
|
|
GenerateMethodBody (minfo, getter, sel, false, var_name, BodyOption.MarkRetDirty, pi);
|
|
}
|
|
if (minfo.is_autorelease) {
|
|
print ("}");
|
|
indent--;
|
|
}
|
|
}
|
|
if (is32BitNotSupported)
|
|
print ("#endif");
|
|
print ("}\n");
|
|
}
|
|
}
|
|
if (pi.CanWrite){
|
|
var setter = pi.GetSetMethod ();
|
|
var ba = GetBindAttribute (setter);
|
|
bool null_allowed = AttributeManager.HasAttribute<NullAllowedAttribute> (setter);
|
|
if (null_allowed)
|
|
ErrorHelper.Show (new BindingException (1118, false, setter));
|
|
null_allowed |= AttributeManager.HasAttribute<NullAllowedAttribute> (pi);
|
|
var not_implemented_attr = AttributeManager.GetCustomAttribute<NotImplementedAttribute> (setter);
|
|
string sel;
|
|
|
|
if (ba == null) {
|
|
sel = GetSetterExportAttribute (pi)?.Selector;
|
|
} else {
|
|
sel = ba.Selector;
|
|
}
|
|
|
|
PrintBlockProxy (pi.PropertyType);
|
|
|
|
// print availability separately since we could be inlining
|
|
#if !NET
|
|
PrintPlatformAttributes (pi, type);
|
|
#endif
|
|
PrintAttributes (pi, platform: false);
|
|
|
|
if (not_implemented_attr == null && (!minfo.is_sealed || !minfo.is_wrapper))
|
|
PrintExport (minfo, sel, export.ArgumentSemantic);
|
|
|
|
PrintAttributes (pi.GetSetMethod (), platform:true, preserve:true, advice:true, notImplemented:true);
|
|
#if NET
|
|
if (false) {
|
|
#else
|
|
if (minfo.is_abstract){
|
|
print ("set; ");
|
|
#endif
|
|
} else {
|
|
print ("set {");
|
|
var is32BitNotSupported = Is64BitiOSOnly (pi);
|
|
if (is32BitNotSupported) {
|
|
print ("#if ARCH_32");
|
|
print ("\tthrow new PlatformNotSupportedException (\"This API is not supported on this version of iOS\");");
|
|
print ("#else");
|
|
}
|
|
if (debug)
|
|
print ("Console.WriteLine (\"In {0}\");", pi.GetSetMethod ());
|
|
|
|
// If we're doing a setter for a weak property that is protocolized event back
|
|
// we need to put in a check to verify you aren't stomping the "internal underscore"
|
|
// generated delegate. We check CheckForEventAndDelegateMismatches global to disable the checks
|
|
if (!BindThirdPartyLibrary && pi.Name.StartsWith ("Weak", StringComparison.Ordinal)) {
|
|
string delName = pi.Name.Substring (4);
|
|
if (SafeIsProtocolizedEventBacked (delName, type))
|
|
print ("\t{0}.EnsureDelegateAssignIsNotOverwritingInternalDelegate ({1}, value, {2});", ApplicationClassName, string.IsNullOrEmpty (var_name) ? "null" : var_name, GetDelegateTypePropertyName (delName));
|
|
}
|
|
|
|
if (not_implemented_attr != null) {
|
|
print ("\tthrow new NotImplementedException ({0});", not_implemented_attr.Message == null ? "" : "\"" + not_implemented_attr.Message + "\"");
|
|
} else if (is_model)
|
|
print ("\tthrow new ModelNotImplementedException ();");
|
|
else if (minfo.is_abstract)
|
|
print ("throw new You_Should_Not_Call_base_In_This_Method ();");
|
|
else {
|
|
GenerateMethodBody (minfo, setter, sel, null_allowed, null, BodyOption.None, pi);
|
|
if (!minfo.is_static && !is_interface_impl && DoesPropertyNeedBackingField (pi)) {
|
|
if (!DoesPropertyNeedDirtyCheck (pi, export)) {
|
|
print ("\tMarkDirty ();");
|
|
print ("\t{0} = value;", var_name);
|
|
}
|
|
}
|
|
}
|
|
if (is32BitNotSupported)
|
|
print ("#endif");
|
|
print ("}");
|
|
}
|
|
}
|
|
indent--;
|
|
print ("}}\n", pi.Name.GetSafeParamName ());
|
|
}
|
|
|
|
class AsyncMethodInfo : MemberInformation {
|
|
public ParameterInfo[] async_initial_params, async_completion_params;
|
|
public bool has_nserror, is_void_async, is_single_arg_async;
|
|
public MethodInfo MethodInfo;
|
|
|
|
public AsyncMethodInfo (Generator generator, IMemberGatherer gather, Type type, MethodInfo mi, Type category_extension_type, bool is_extension_method)
|
|
: base (generator, gather, mi, type, category_extension_type, false, is_extension_method)
|
|
{
|
|
this.MethodInfo = mi;
|
|
this.async_initial_params = Generator.DropLast (mi.GetParameters ());
|
|
|
|
var lastType = mi.GetParameters ().Last ().ParameterType;
|
|
if (!lastType.IsSubclassOf (generator.TypeManager.System_Delegate))
|
|
throw new BindingException (1036, true, mi.DeclaringType.FullName, mi.Name, lastType.FullName);
|
|
var cbParams = lastType.GetMethod ("Invoke").GetParameters ();
|
|
async_completion_params = cbParams;
|
|
|
|
// ?!? this fails: cbParams.Last ().ParameterType.Name == TypeManager.NSError
|
|
if (cbParams.Length > 0 && cbParams.Last ().ParameterType.Name == "NSError") {
|
|
has_nserror = true;
|
|
cbParams = Generator.DropLast (cbParams);
|
|
}
|
|
if (cbParams.Length == 0)
|
|
is_void_async = true;
|
|
if (cbParams.Length == 1)
|
|
is_single_arg_async = true;
|
|
}
|
|
|
|
public string GetUniqueParamName (string suggestion)
|
|
{
|
|
while (true) {
|
|
bool next = false;
|
|
|
|
foreach (var pi in async_completion_params) {
|
|
if (pi.Name == suggestion) {
|
|
next = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!next)
|
|
return suggestion;
|
|
|
|
suggestion = "_" + suggestion;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
public static T[] DropLast<T> (T[] arr)
|
|
{
|
|
T[] res = new T [arr.Length - 1];
|
|
Array.Copy (arr, res, res.Length);
|
|
return res;
|
|
}
|
|
|
|
string GetReturnType (AsyncMethodInfo minfo)
|
|
{
|
|
if (minfo.is_void_async)
|
|
return "Task";
|
|
var ttype = GetAsyncTaskType (minfo);
|
|
if (minfo.has_nserror && (ttype == "bool"))
|
|
ttype = "Tuple<bool,NSError>";
|
|
return "Task<" + ttype + ">";
|
|
}
|
|
|
|
string GetAsyncTaskType (AsyncMethodInfo minfo)
|
|
{
|
|
if (minfo.is_single_arg_async)
|
|
return FormatType (minfo.type, minfo.async_completion_params [0].ParameterType);
|
|
|
|
var attr = AttributeManager.GetCustomAttribute<AsyncAttribute> (minfo.mi);
|
|
if (attr.ResultTypeName != null)
|
|
return attr.ResultTypeName;
|
|
if (attr.ResultType != null)
|
|
return FormatType (minfo.type, attr.ResultType);
|
|
|
|
//Console.WriteLine ("{0}", minfo.MethodInfo.GetParameters ().Last ().ParameterType);
|
|
throw new BindingException (1077, true, minfo.mi);
|
|
}
|
|
|
|
string GetInvokeParamList (ParameterInfo [] parameters, bool suffix = true, bool force = false)
|
|
{
|
|
StringBuilder sb = new StringBuilder ();
|
|
bool comma = false;
|
|
foreach (var pi in parameters) {
|
|
if (comma) {
|
|
sb.Append (", ");
|
|
}
|
|
comma = true;
|
|
sb.Append (pi.Name.GetSafeParamName ());
|
|
if (suffix)
|
|
sb.Append ('_');
|
|
if (force)
|
|
sb.Append ('!');
|
|
}
|
|
return sb.ToString ();
|
|
}
|
|
|
|
//
|
|
// The kind of Async method we generate
|
|
// We typically generate a single one, but if the
|
|
// async method has a non-void return, we generate the second with the out parameter
|
|
//
|
|
enum AsyncMethodKind {
|
|
// Plain Async method, original method return void
|
|
Plain,
|
|
|
|
// Async method generated when we had a return type from the method
|
|
// ie: [Async] string XyZ (Action completion), the "string" is the
|
|
// result
|
|
WithResultOutParameter,
|
|
}
|
|
|
|
void PrintAsyncHeader (AsyncMethodInfo minfo, AsyncMethodKind asyncKind)
|
|
{
|
|
print_generated_code ();
|
|
string extra = "";
|
|
|
|
if (asyncKind == AsyncMethodKind.WithResultOutParameter) {
|
|
if (minfo.method.GetParameters ().Count () > 1)
|
|
extra = ", ";
|
|
extra += "out " + FormatType (minfo.MethodInfo.DeclaringType, minfo.MethodInfo.ReturnType) + " " + minfo.GetUniqueParamName ("result");
|
|
}
|
|
|
|
// async wrapper don't have to be abstract (it's counter productive)
|
|
var modifier = minfo.GetModifiers ().Replace ("abstract ", String.Empty);
|
|
|
|
print ("{0} {1}{2} {3}",
|
|
minfo.GetVisibility (),
|
|
modifier,
|
|
GetReturnType (minfo),
|
|
MakeSignature (minfo, true, minfo.async_initial_params, extra),
|
|
minfo.is_abstract ? ";" : "");
|
|
}
|
|
|
|
void GenerateAsyncMethod (MemberInformation original_minfo, AsyncMethodKind asyncKind)
|
|
{
|
|
var mi = original_minfo.method;
|
|
var minfo = new AsyncMethodInfo (this, this, original_minfo.type, mi, original_minfo.category_extension_type, original_minfo.is_extension_method);
|
|
var is_void = mi.ReturnType == TypeManager.System_Void;
|
|
|
|
// Print a error if any of the method parameters or handler parameters is ref/out, it should not be asyncified.
|
|
if (minfo.async_initial_params != null) {
|
|
foreach (var param in minfo.async_initial_params) {
|
|
if (param.ParameterType.IsByRef) {
|
|
throw new BindingException (1062, true, original_minfo.type.Name, mi.Name);
|
|
}
|
|
}
|
|
}
|
|
foreach (var param in minfo.async_completion_params) {
|
|
if (param.ParameterType.IsByRef) {
|
|
throw new BindingException (1062, true, original_minfo.type.Name, mi.Name);
|
|
}
|
|
}
|
|
|
|
PrintMethodAttributes (minfo);
|
|
|
|
PrintAsyncHeader (minfo, asyncKind);
|
|
|
|
print ("{");
|
|
indent++;
|
|
|
|
var ttype = "bool";
|
|
var tuple = false;
|
|
if (!minfo.is_void_async) {
|
|
ttype = GetAsyncTaskType (minfo);
|
|
tuple = (minfo.has_nserror && (ttype == "bool"));
|
|
if (tuple)
|
|
ttype = "Tuple<bool,NSError>";
|
|
}
|
|
print ("var tcs = new TaskCompletionSource<{0}> ();", ttype);
|
|
bool ignoreResult = !is_void &&
|
|
asyncKind == AsyncMethodKind.Plain &&
|
|
AttributeManager.GetCustomAttribute<AsyncAttribute> (mi).PostNonResultSnippet == null;
|
|
print ("{6}{5}{4}{0}({1}{2}({3}) => {{",
|
|
mi.Name,
|
|
GetInvokeParamList (minfo.async_initial_params, false),
|
|
minfo.async_initial_params.Length > 0 ? ", " : "",
|
|
GetInvokeParamList (minfo.async_completion_params),
|
|
minfo.is_extension_method || minfo.is_category_extension ? "This." : string.Empty,
|
|
is_void || ignoreResult ? string.Empty : minfo.GetUniqueParamName ("result") + " = ",
|
|
is_void || ignoreResult ? string.Empty : (asyncKind == AsyncMethodKind.WithResultOutParameter ? string.Empty : "var ")
|
|
);
|
|
|
|
indent++;
|
|
|
|
int nesting_level = 1;
|
|
if (minfo.has_nserror && !tuple) {
|
|
var var_name = minfo.async_completion_params.Last ().Name.GetSafeParamName ();;
|
|
print ("if ({0}_ != null)", var_name);
|
|
print ("\ttcs.SetException (new NSErrorException({0}_));", var_name);
|
|
print ("else");
|
|
++nesting_level; ++indent;
|
|
}
|
|
|
|
if (minfo.is_void_async)
|
|
print ("tcs.SetResult (true);");
|
|
else if (tuple) {
|
|
var cond_name = minfo.async_completion_params [0].Name;
|
|
var var_name = minfo.async_completion_params.Last ().Name;
|
|
print ("tcs.SetResult (new Tuple<bool,NSError> ({0}_, {1}_));", cond_name, var_name);
|
|
} else if (minfo.is_single_arg_async)
|
|
print ("tcs.SetResult ({0}_!);", minfo.async_completion_params [0].Name);
|
|
else
|
|
print ("tcs.SetResult (new {0} ({1}));",
|
|
GetAsyncTaskType (minfo),
|
|
GetInvokeParamList (minfo.has_nserror ? DropLast (minfo.async_completion_params) : minfo.async_completion_params, true, true));
|
|
indent -= nesting_level;
|
|
if (is_void || ignoreResult)
|
|
print ("});");
|
|
else
|
|
print ("})!;");
|
|
var attr = AttributeManager.GetCustomAttribute<AsyncAttribute> (mi);
|
|
if (asyncKind == AsyncMethodKind.Plain && !string.IsNullOrEmpty (attr.PostNonResultSnippet))
|
|
print (attr.PostNonResultSnippet);
|
|
print ("return tcs.Task;");
|
|
indent--;
|
|
print ("}\n");
|
|
|
|
|
|
if (attr.ResultTypeName != null) {
|
|
if (minfo.has_nserror)
|
|
async_result_types.Add (new Tuple<string, ParameterInfo[]> (attr.ResultTypeName, DropLast (minfo.async_completion_params)));
|
|
else
|
|
async_result_types.Add (new Tuple<string, ParameterInfo[]> (attr.ResultTypeName, minfo.async_completion_params));
|
|
}
|
|
}
|
|
|
|
void PrintMethodAttributes (MemberInformation minfo)
|
|
{
|
|
MethodInfo mi = minfo.method;
|
|
var editor_browsable_attribute = false;
|
|
|
|
foreach (var sa in AttributeManager.GetCustomAttributes<ThreadSafeAttribute> (mi))
|
|
print (sa.Safe ? "[ThreadSafe]" : "[ThreadSafe (false)]");
|
|
|
|
foreach (var ea in AttributeManager.GetCustomAttributes<EditorBrowsableAttribute> (mi)) {
|
|
if (ea.State == EditorBrowsableState.Always) {
|
|
print ("[EditorBrowsable]");
|
|
} else {
|
|
print ("[EditorBrowsable (EditorBrowsableState.{0})]", ea.State);
|
|
}
|
|
editor_browsable_attribute = true;
|
|
}
|
|
|
|
foreach (var oa in AttributeManager.GetCustomAttributes<ObsoleteAttribute> (mi)) {
|
|
print ("[Obsolete (\"{0}\", {1})]", oa.Message, oa.IsError ? "true" : "false");
|
|
if (!editor_browsable_attribute)
|
|
print ("[EditorBrowsable (EditorBrowsableState.Never)]");
|
|
}
|
|
|
|
if (minfo.is_return_release)
|
|
print ("[return: ReleaseAttribute ()]");
|
|
|
|
// when we inline methods (e.g. from a protocol)
|
|
if (minfo.type != minfo.method.DeclaringType) {
|
|
// we must look if the type has an [Availability] attribute
|
|
// but we must not duplicate existing attributes for a platform, see https://github.com/xamarin/xamarin-macios/issues/7194
|
|
PrintPlatformAttributesNoDuplicates (minfo.type, minfo.method);
|
|
} else {
|
|
PrintPlatformAttributes (minfo.method);
|
|
}
|
|
|
|
// in theory we could check for `minfo.is_ctor` but some manual bindings are using methods for `init*`
|
|
if (AttributeManager.HasAttribute<DesignatedInitializerAttribute> (mi))
|
|
print ("[DesignatedInitializer]");
|
|
}
|
|
|
|
|
|
void GenerateMethod (Type type, MethodInfo mi, bool is_model, Type category_extension_type, bool is_appearance, bool is_interface_impl = false, bool is_extension_method = false, string selector = null, bool isBaseWrapperProtocolMethod = false)
|
|
{
|
|
var minfo = new MemberInformation (this, this, mi, type, category_extension_type, is_interface_impl, is_extension_method, is_appearance, is_model, selector, isBaseWrapperProtocolMethod);
|
|
GenerateMethod (minfo);
|
|
}
|
|
|
|
void PrintDelegateProxy (MemberInformation minfo)
|
|
{
|
|
PrintDelegateProxy (minfo.method);
|
|
}
|
|
|
|
void PrintDelegateProxy (MethodInfo mi)
|
|
{
|
|
if (mi.ReturnType.IsSubclassOf (TypeManager.System_Delegate)) {
|
|
var ti = MakeTrampoline (mi.ReturnType);
|
|
print ("[return: DelegateProxy (typeof (ObjCRuntime.Trampolines.{0}))]", ti.StaticName);
|
|
}
|
|
}
|
|
|
|
void PrintBlockProxy (Type type)
|
|
{
|
|
type = GetCorrectGenericType (type);
|
|
if (type.IsSubclassOf (TypeManager.System_Delegate)) {
|
|
var ti = MakeTrampoline (type);
|
|
print ("[param: BlockProxy (typeof (ObjCRuntime.Trampolines.{0}))]", ti.NativeInvokerName);
|
|
}
|
|
}
|
|
|
|
void PrintExport (MemberInformation minfo)
|
|
{
|
|
if (minfo.is_export)
|
|
print ("[Export (\"{0}\"{1})]", minfo.selector, minfo.is_variadic ? ", IsVariadic = true" : string.Empty);
|
|
}
|
|
|
|
void PrintExport (MemberInformation minfo, ExportAttribute ea)
|
|
{
|
|
PrintExport (minfo, ea.Selector, ea.ArgumentSemantic);
|
|
}
|
|
|
|
void PrintExport (MemberInformation minfo, string sel, ArgumentSemantic semantic)
|
|
{
|
|
bool output_semantics = semantic != ArgumentSemantic.None;
|
|
// it does not make sense on every properties, depending on the their types
|
|
if (output_semantics && (minfo.mi is PropertyInfo)) {
|
|
var t = minfo.property.PropertyType;
|
|
output_semantics = !t.IsPrimitive || t == TypeManager.System_IntPtr;
|
|
}
|
|
|
|
if (output_semantics)
|
|
print ("[Export (\"{0}\", ArgumentSemantic.{1})]", sel, semantic);
|
|
else
|
|
print ("[Export (\"{0}\")]", sel);
|
|
}
|
|
|
|
void GenerateMethod (MemberInformation minfo)
|
|
{
|
|
var mi = minfo.method;
|
|
|
|
// skip if we provide a manual implementation that would conflict with the generated code
|
|
if (AttributeManager.HasAttribute<ManualAttribute> (mi))
|
|
return;
|
|
|
|
foreach (ParameterInfo pi in mi.GetParameters ())
|
|
if (AttributeManager.HasAttribute<RetainAttribute> (pi)) {
|
|
print ("#pragma warning disable 168");
|
|
print ("{0}? __mt_{1}_{2};", pi.ParameterType, mi.Name, pi.Name);
|
|
print ("#pragma warning restore 168");
|
|
}
|
|
|
|
int argCount = 0;
|
|
if (minfo.is_export && !minfo.is_variadic) {
|
|
foreach (char c in minfo.selector){
|
|
if (c == ':')
|
|
argCount++;
|
|
}
|
|
if (minfo.method.GetParameters ().Length != argCount) {
|
|
ErrorHelper.Warning (1105,
|
|
minfo.selector, argCount, minfo.method, minfo.method.GetParameters ().Length);
|
|
}
|
|
}
|
|
|
|
PrintDelegateProxy (minfo);
|
|
|
|
if (AttributeManager.HasAttribute<NoMethodAttribute>(minfo.mi)){
|
|
// Call for side effect
|
|
MakeSignature (minfo);
|
|
return;
|
|
}
|
|
|
|
PrintExport (minfo);
|
|
|
|
if (!minfo.is_interface_impl) {
|
|
PrintMethodAttributes (minfo);
|
|
} else if (minfo.is_return_release) {
|
|
print ("[return: ReleaseAttribute ()]");
|
|
}
|
|
|
|
var mod = minfo.GetVisibility ();
|
|
|
|
#if NET
|
|
var is_abstract = false;
|
|
var do_not_call_base = minfo.is_abstract || minfo.is_model;
|
|
#else
|
|
var is_abstract = minfo.is_abstract;
|
|
var do_not_call_base = minfo.is_model;
|
|
#endif
|
|
print_generated_code (optimizable: IsOptimizable (minfo.mi));
|
|
print ("{0} {1}{2}{3}",
|
|
mod,
|
|
minfo.GetModifiers (),
|
|
MakeSignature (minfo),
|
|
is_abstract ? ";" : "");
|
|
|
|
|
|
if (!is_abstract){
|
|
if (minfo.is_ctor) {
|
|
indent++;
|
|
print (": {0}", minfo.wrap_method == null ? "base (NSObjectFlag.Empty)" : minfo.wrap_method);
|
|
indent--;
|
|
}
|
|
|
|
print ("{");
|
|
|
|
var is32BitNotSupported = Is64BitiOSOnly ((ICustomAttributeProvider) minfo.method ?? minfo.property);
|
|
if (is32BitNotSupported) {
|
|
print ("#if ARCH_32");
|
|
print ("\tthrow new PlatformNotSupportedException (\"This API is not supported on this version of iOS\");");
|
|
print ("#else");
|
|
}
|
|
if (debug)
|
|
print ("\tConsole.WriteLine (\"In {0}\");", mi);
|
|
|
|
if (do_not_call_base)
|
|
print ("\tthrow new You_Should_Not_Call_base_In_This_Method ();");
|
|
else if (minfo.wrap_method != null) {
|
|
if (!minfo.is_ctor) {
|
|
indent++;
|
|
|
|
string ret = mi.ReturnType == TypeManager.System_Void ? null : "return ";
|
|
print ("{0}{1}{2};", ret, minfo.is_extension_method ? "This." : "", minfo.wrap_method);
|
|
indent--;
|
|
}
|
|
} else {
|
|
if (minfo.is_autorelease) {
|
|
indent++;
|
|
print ("using (var autorelease_pool = new NSAutoreleasePool ()) {");
|
|
}
|
|
PropertyInfo pinfo = null;
|
|
MethodInfo method = minfo.method;
|
|
bool null_allowed = false;
|
|
if (method.IsSpecialName) {
|
|
// could be a property setter where [NullAllowed] is _allowed_
|
|
// we do not need the information if it's a getter (it won't change generated code)
|
|
pinfo = GetProperty (method, getter: false, setter: true);
|
|
if (pinfo != null)
|
|
null_allowed = AttributeManager.HasAttribute<NullAllowedAttribute> (pinfo);
|
|
}
|
|
GenerateMethodBody (minfo, minfo.method, minfo.selector, null_allowed, null, BodyOption.None, pinfo);
|
|
if (minfo.is_autorelease) {
|
|
print ("}");
|
|
indent--;
|
|
}
|
|
}
|
|
if (is32BitNotSupported)
|
|
print ("#endif");
|
|
print ("}\n");
|
|
}
|
|
|
|
if (AttributeManager.HasAttribute<AsyncAttribute> (mi)) {
|
|
// We do not want Async methods inside internal wrapper classes, they are useless
|
|
// internal sealed class FooWrapper : BaseWrapper, IMyFooDelegate
|
|
// Also we do not want Async members inside [Model] classes
|
|
if (minfo.is_basewrapper_protocol_method || minfo.is_model)
|
|
return;
|
|
|
|
GenerateAsyncMethod (minfo, AsyncMethodKind.Plain);
|
|
|
|
// Generate the overload with the out parameter
|
|
if (minfo.method.ReturnType != TypeManager.System_Void){
|
|
GenerateAsyncMethod (minfo, AsyncMethodKind.WithResultOutParameter);
|
|
}
|
|
}
|
|
}
|
|
|
|
static PropertyInfo GetProperty (MethodInfo method, bool getter = true, bool setter = true)
|
|
{
|
|
var props = method.DeclaringType.GetProperties ();
|
|
if (method.GetParameters ().Length == 0)
|
|
return !getter ? null : props.FirstOrDefault (prop => prop.GetGetMethod () == method);
|
|
else
|
|
return !setter ? null : props.FirstOrDefault (prop => prop.GetSetMethod () == method);
|
|
}
|
|
|
|
public string GetGeneratedTypeName (Type type)
|
|
{
|
|
var bindOnType = AttributeManager.GetCustomAttributes<BindAttribute> (type);
|
|
if (bindOnType.Length > 0)
|
|
return bindOnType [0].Selector;
|
|
else if (type.IsGenericTypeDefinition)
|
|
return type.Name.Substring (0, type.Name.IndexOf ('`'));
|
|
else
|
|
return type.Name;
|
|
}
|
|
|
|
void RenderDelegates (Dictionary<string,MethodInfo> delegateTypes)
|
|
{
|
|
// Group the delegates by namespace
|
|
var groupedTypes = from fullname in delegateTypes.Keys
|
|
where fullname != "System.Action"
|
|
let p = fullname.LastIndexOf ('.')
|
|
let ns = p == -1 ? String.Empty : fullname.Substring (0, p)
|
|
group fullname by ns into g
|
|
select new {Namespace = g.Key, Fullname=g};
|
|
|
|
foreach (var group in groupedTypes.OrderBy (v => v.Namespace, StringComparer.Ordinal)) {
|
|
if (group.Namespace != null) {
|
|
print ("namespace {0} {{", group.Namespace);
|
|
indent++;
|
|
}
|
|
print ("#nullable enable");
|
|
|
|
foreach (var deltype in group.Fullname.OrderBy (v => v, StringComparer.Ordinal)) {
|
|
int p = deltype.LastIndexOf ('.');
|
|
var shortName = deltype.Substring (p+1);
|
|
var mi = delegateTypes [deltype];
|
|
|
|
if (shortName.StartsWith ("Func<", StringComparison.Ordinal))
|
|
continue;
|
|
|
|
var del = mi.DeclaringType;
|
|
|
|
if (AttributeManager.HasAttribute (mi.DeclaringType, "MonoNativeFunctionWrapper"))
|
|
print ("[MonoNativeFunctionWrapper]\n");
|
|
|
|
print ("public delegate {0} {1} ({2});",
|
|
RenderType (mi.ReturnType, mi.ReturnTypeCustomAttributes),
|
|
shortName,
|
|
RenderParameterDecl (mi.GetParameters ()));
|
|
}
|
|
|
|
if (group.Namespace != null) {
|
|
indent--;
|
|
print ("}\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
IEnumerable<MethodInfo> SelectProtocolMethods (Type type, bool? @static = null, bool? required = null)
|
|
{
|
|
var list = type.GetMethods (BindingFlags.Public | BindingFlags.Instance);
|
|
|
|
foreach (var m in list) {
|
|
if (m.IsSpecialName)
|
|
continue;
|
|
|
|
if (m.Name == "Constructor")
|
|
continue;
|
|
|
|
var attrs = AttributeManager.GetCustomAttributes<Attribute> (m);
|
|
AvailabilityBaseAttribute availabilityAttribute = null;
|
|
bool hasExportAttribute = false;
|
|
bool hasStaticAttribute = false;
|
|
|
|
foreach (var a in attrs){
|
|
if (a is AvailabilityBaseAttribute){
|
|
availabilityAttribute = a as AvailabilityBaseAttribute;
|
|
} else if (a is ExportAttribute)
|
|
hasExportAttribute = true;
|
|
else if (a is StaticAttribute)
|
|
hasStaticAttribute = true;
|
|
}
|
|
if (availabilityAttribute != null && m.IsUnavailable (this))
|
|
continue;
|
|
|
|
if (!hasExportAttribute)
|
|
continue;
|
|
|
|
if (@static.HasValue && @static.Value != hasStaticAttribute)
|
|
continue;
|
|
|
|
if (required.HasValue && required.Value != IsRequired (m, attrs))
|
|
continue;
|
|
|
|
yield return m;
|
|
}
|
|
}
|
|
|
|
IEnumerable<PropertyInfo> SelectProtocolProperties (Type type, bool? @static = null, bool? required = null)
|
|
{
|
|
var list = type.GetProperties (BindingFlags.Public | BindingFlags.Instance);
|
|
|
|
foreach (var p in list) {
|
|
var attrs = AttributeManager.GetCustomAttributes<Attribute> (p);
|
|
AvailabilityBaseAttribute availabilityAttribute = null;
|
|
bool hasExportAttribute = false;
|
|
bool hasStaticAttribute = false;
|
|
|
|
foreach (var a in attrs){
|
|
if (a is AvailabilityBaseAttribute){
|
|
availabilityAttribute = a as AvailabilityBaseAttribute;
|
|
} else if (a is ExportAttribute)
|
|
hasExportAttribute = true;
|
|
else if (a is StaticAttribute)
|
|
hasStaticAttribute = true;
|
|
}
|
|
if (availabilityAttribute != null && p.IsUnavailable (this))
|
|
continue;
|
|
|
|
if (!hasExportAttribute) {
|
|
var getter = p.GetGetMethod ();
|
|
if (p.CanRead && !AttributeManager.HasAttribute<ExportAttribute> (getter)) {
|
|
if (!AttributeManager.HasAttribute<NotImplementedAttribute> (getter))
|
|
continue;
|
|
}
|
|
var setter = p.GetSetMethod ();
|
|
if (p.CanWrite && !AttributeManager.HasAttribute<ExportAttribute> (setter)) {
|
|
if (!AttributeManager.HasAttribute<NotImplementedAttribute> (setter))
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (@static.HasValue && @static.Value != hasStaticAttribute)
|
|
continue;
|
|
|
|
if (required.HasValue && required.Value != IsRequired (p, attrs))
|
|
continue;
|
|
|
|
yield return p;
|
|
}
|
|
}
|
|
|
|
// If a type comes from the assembly with the api definition we're processing.
|
|
bool IsApiType (Type type)
|
|
{
|
|
return type.Assembly == types [0].Assembly;
|
|
}
|
|
|
|
bool IsRequired (MemberInfo provider, Attribute [] attributes = null)
|
|
{
|
|
var type = provider.DeclaringType;
|
|
if (IsApiType (type)){
|
|
return HasAttribute<AbstractAttribute> (provider, attributes);
|
|
}
|
|
if (type.IsInterface)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
void GenerateProtocolTypes (Type type, string class_visibility, string TypeName, string protocol_name, ProtocolAttribute protocolAttribute)
|
|
{
|
|
var allProtocolMethods = new List<MethodInfo> ();
|
|
var allProtocolProperties = new List<PropertyInfo> ();
|
|
var ifaces = (IEnumerable<Type>) type.GetInterfaces ().Concat (new Type [] { ReflectionExtensions.GetBaseType (type, this) }).OrderBy (v => v.FullName, StringComparer.Ordinal);
|
|
|
|
if (type.Namespace != null) {
|
|
print ("namespace {0} {{", type.Namespace);
|
|
indent++;
|
|
}
|
|
|
|
ifaces = ifaces.Where ((v) => IsProtocolInterface (v, false));
|
|
|
|
allProtocolMethods.AddRange (SelectProtocolMethods (type));
|
|
allProtocolProperties.AddRange (SelectProtocolProperties (type));
|
|
|
|
var requiredInstanceMethods = allProtocolMethods.Where ((v) => IsRequired (v) && !AttributeManager.HasAttribute<StaticAttribute> (v)).ToList ();
|
|
var optionalInstanceMethods = allProtocolMethods.Where ((v) => !IsRequired (v) && !AttributeManager.HasAttribute<StaticAttribute> (v));
|
|
var requiredInstanceProperties = allProtocolProperties.Where ((v) => IsRequired (v) && !AttributeManager.HasAttribute<StaticAttribute> (v)).ToList ();
|
|
var optionalInstanceProperties = allProtocolProperties.Where ((v) => !IsRequired (v) && !AttributeManager.HasAttribute<StaticAttribute> (v));
|
|
var requiredInstanceAsyncMethods = requiredInstanceMethods.Where (m => AttributeManager.HasAttribute<AsyncAttribute> (m)).ToList ();
|
|
|
|
PrintAttributes (type, platform:true, preserve:true, advice:true);
|
|
print ("[Protocol (Name = \"{1}\", WrapperType = typeof ({0}Wrapper){2}{3})]",
|
|
TypeName,
|
|
protocol_name,
|
|
protocolAttribute.IsInformal ? ", IsInformal = true" : string.Empty,
|
|
protocolAttribute.FormalSince != null ? $", FormalSince = \"{protocolAttribute.FormalSince}\"" : string.Empty);
|
|
|
|
var sb = new StringBuilder ();
|
|
|
|
foreach (var mi in allProtocolMethods) {
|
|
var attrib = GetExportAttribute (mi);
|
|
sb.Clear ();
|
|
sb.Append ("[ProtocolMember (");
|
|
sb.Append ("IsRequired = ").Append (IsRequired (mi) ? "true" : "false");
|
|
sb.Append (", IsProperty = false");
|
|
sb.Append (", IsStatic = ").Append (AttributeManager.HasAttribute<StaticAttribute> (mi) ? "true" : "false");
|
|
sb.Append (", Name = \"").Append (mi.Name).Append ("\"");
|
|
sb.Append (", Selector = \"").Append (attrib.Selector).Append ("\"");
|
|
if (mi.ReturnType != TypeManager.System_Void) {
|
|
var retType = GetCorrectGenericType (mi.ReturnType);
|
|
sb.Append (", ReturnType = typeof (").Append (RenderType (retType)).Append (")");
|
|
if (retType.IsSubclassOf (TypeManager.System_Delegate)) {
|
|
var ti = MakeTrampoline (retType);
|
|
sb.Append ($", ReturnTypeDelegateProxy = typeof (ObjCRuntime.Trampolines.{ti.StaticName})");
|
|
}
|
|
}
|
|
var parameters = mi.GetParameters ();
|
|
if (parameters != null && parameters.Length > 0) {
|
|
sb.Append (", ParameterType = new Type [] { ");
|
|
for (int i = 0; i < parameters.Length; i++) {
|
|
if (i > 0)
|
|
sb.Append (", ");
|
|
sb.Append ("typeof (");
|
|
var pt = parameters [i].ParameterType;
|
|
if (pt.IsByRef)
|
|
pt = pt.GetElementType ();
|
|
sb.Append (RenderType (GetCorrectGenericType (pt)));
|
|
sb.Append (")");
|
|
}
|
|
sb.Append (" }");
|
|
sb.Append (", ParameterByRef = new bool [] { ");
|
|
for (int i = 0; i < parameters.Length; i++) {
|
|
if (i > 0)
|
|
sb.Append (", ");
|
|
sb.Append (parameters [i].ParameterType.IsByRef ? "true" : "false");
|
|
}
|
|
sb.Append (" }");
|
|
|
|
var blockProxies = new string [parameters.Length];
|
|
var anyblockProxy = false;
|
|
for (int i = 0; i < parameters.Length; i++) {
|
|
var parType = GetCorrectGenericType (parameters [i].ParameterType);
|
|
if (parType.IsSubclassOf (TypeManager.System_Delegate)) {
|
|
var ti = MakeTrampoline (parType);
|
|
blockProxies [i] = $"typeof (ObjCRuntime.Trampolines.{ti.NativeInvokerName})";
|
|
anyblockProxy = true;
|
|
}
|
|
}
|
|
if (anyblockProxy) {
|
|
sb.Append (", ParameterBlockProxy = new Type? [] { ");
|
|
for (int i = 0; i < blockProxies.Length; i++) {
|
|
if (i > 0)
|
|
sb.Append (", ");
|
|
sb.Append (blockProxies [i] == null ? "null" : blockProxies [i]);
|
|
}
|
|
sb.Append (" }");
|
|
}
|
|
|
|
if (attrib.IsVariadic)
|
|
sb.Append (", IsVariadic = true");
|
|
}
|
|
sb.Append (")]");
|
|
print (sb.ToString ());
|
|
}
|
|
foreach (var pi in allProtocolProperties) {
|
|
var attrib = GetExportAttribute (pi);
|
|
sb.Clear ();
|
|
sb.Append ("[ProtocolMember (");
|
|
sb.Append ("IsRequired = ").Append (IsRequired (pi) ? "true" : "false");
|
|
sb.Append (", IsProperty = true");
|
|
sb.Append (", IsStatic = ").Append (AttributeManager.HasAttribute<StaticAttribute> (pi) ? "true" : "false");
|
|
sb.Append (", Name = \"").Append (pi.Name).Append ("\"");
|
|
sb.Append (", Selector = \"").Append (attrib.Selector).Append ("\"");
|
|
sb.Append (", PropertyType = typeof (").Append (RenderType (pi.PropertyType)).Append(")");
|
|
if (pi.CanRead && !AttributeManager.HasAttribute<NotImplementedAttribute> (pi.GetGetMethod ())) {
|
|
var ea = GetGetterExportAttribute (pi);
|
|
var ba = GetBindAttribute (pi.GetGetMethod ());
|
|
sb.Append (", GetterSelector = \"").Append (ba != null ? ba.Selector : ea.Selector).Append ("\"");
|
|
}
|
|
if (pi.CanWrite && !AttributeManager.HasAttribute<NotImplementedAttribute> (pi.GetSetMethod ())) {
|
|
var ea = GetSetterExportAttribute (pi);
|
|
var ba = GetBindAttribute (pi.GetSetMethod ());
|
|
sb.Append (", SetterSelector = \"").Append (ba != null ? ba.Selector : ea.Selector).Append ("\"");
|
|
}
|
|
sb.Append (", ArgumentSemantic = ArgumentSemantic.").Append (attrib.ArgumentSemantic);
|
|
// Check for block/delegate proxies
|
|
var propType = GetCorrectGenericType (pi.PropertyType);
|
|
if (propType.IsSubclassOf (TypeManager.System_Delegate)) {
|
|
var ti = MakeTrampoline (propType);
|
|
if (pi.SetMethod != null)
|
|
sb.Append ($", ParameterBlockProxy = new Type [] {{ typeof (ObjCRuntime.Trampolines.{ti.NativeInvokerName}) }}");
|
|
if (pi.GetMethod != null)
|
|
sb.Append ($", ReturnTypeDelegateProxy = typeof (ObjCRuntime.Trampolines.{ti.StaticName})");
|
|
}
|
|
sb.Append (")]");
|
|
print (sb.ToString ());
|
|
}
|
|
|
|
PrintXpcInterfaceAttribute (type);
|
|
print ("{0} partial interface I{1} : INativeObject, IDisposable{2}", class_visibility, TypeName, ifaces.Count () > 0 ? ", " : string.Empty);
|
|
indent++;
|
|
sb.Clear ();
|
|
foreach (var iface in ifaces) {
|
|
string iname = iface.Name;
|
|
// if the [Protocol] interface is defined in the binding itself it won't start with an 'I'
|
|
// but if it's a reference to something inside an already built assembly,
|
|
// e.g. monotouch.dll, then the 'I' will be present
|
|
if (sb.Length > 0)
|
|
sb.Append (", ");
|
|
sb.Append (iface.Namespace).Append ('.');
|
|
if (iface.Assembly.GetName ().Name == "temp")
|
|
sb.Append ('I');
|
|
else if (iface.IsClass)
|
|
sb.Append ('I');
|
|
sb.AppendLine (iname);
|
|
}
|
|
if (sb.Length > 0)
|
|
print (sb.ToString ());
|
|
indent--;
|
|
|
|
print ("{");
|
|
indent++;
|
|
foreach (var mi in requiredInstanceMethods) {
|
|
if (AttributeManager.HasAttribute<StaticAttribute> (mi))
|
|
continue;
|
|
|
|
var minfo = new MemberInformation (this, this, mi, type, null);
|
|
var mod = string.Empty;
|
|
|
|
PrintMethodAttributes (minfo);
|
|
print_generated_code ();
|
|
PrintDelegateProxy (minfo);
|
|
PrintExport (minfo);
|
|
print ("[Preserve (Conditional = true)]");
|
|
if (minfo.is_unsafe)
|
|
mod = "unsafe ";
|
|
print ("{0}{1};", mod, MakeSignature (minfo, true));
|
|
print ("");
|
|
}
|
|
|
|
foreach (var pi in requiredInstanceProperties) {
|
|
var minfo = new MemberInformation (this, this, pi, type);
|
|
var mod = string.Empty;
|
|
minfo.is_export = true;
|
|
|
|
print ("[Preserve (Conditional = true)]");
|
|
|
|
if (minfo.is_unsafe)
|
|
mod = "unsafe ";
|
|
// IsValueType check needed for `IntPtr` signatures (which can't become `IntPtr?`)
|
|
var nullable = !pi.PropertyType.IsValueType && AttributeManager.HasAttribute<NullAllowedAttribute> (pi) ? "?" : String.Empty;
|
|
print ("{0}{1}{2} {3} {{", mod, FormatType (type, pi.PropertyType), nullable, pi.Name, pi.CanRead ? "get;" : string.Empty, pi.CanWrite ? "set;" : string.Empty);
|
|
indent++;
|
|
if (pi.CanRead) {
|
|
var ea = GetGetterExportAttribute (pi);
|
|
var getMethod = pi.GetGetMethod ();
|
|
// there can be a [Bind] there that override the selector name to be used
|
|
// e.g. IMTLTexture.FramebufferOnly
|
|
var ba = GetBindAttribute (getMethod);
|
|
PrintDelegateProxy (getMethod);
|
|
if (!AttributeManager.HasAttribute<NotImplementedAttribute> (getMethod)) {
|
|
if (ba != null)
|
|
PrintExport (minfo, ba.Selector, ea.ArgumentSemantic);
|
|
else
|
|
PrintExport (minfo, ea);
|
|
}
|
|
PrintAttributes (getMethod, notImplemented: true);
|
|
print ("get;");
|
|
}
|
|
if (pi.CanWrite) {
|
|
var setMethod = pi.GetSetMethod ();
|
|
PrintBlockProxy (pi.PropertyType);
|
|
PrintAttributes (setMethod, notImplemented:true);
|
|
if (!AttributeManager.HasAttribute<NotImplementedAttribute> (setMethod))
|
|
PrintExport (minfo, GetSetterExportAttribute (pi));
|
|
print ("set;");
|
|
}
|
|
indent--;
|
|
print ("}");
|
|
print ("");
|
|
}
|
|
indent--;
|
|
print ("}");
|
|
print ("");
|
|
|
|
// avoid (for unified) all the metadata for empty static classes, we can introduce them later when required
|
|
bool include_extensions = false;
|
|
include_extensions = optionalInstanceMethods.Any () || optionalInstanceProperties.Any () || requiredInstanceAsyncMethods.Any ();
|
|
if (include_extensions) {
|
|
// extension methods
|
|
PrintAttributes (type, preserve:true, advice:true);
|
|
print ("{1} static partial class {0}_Extensions {{", TypeName, class_visibility);
|
|
indent++;
|
|
foreach (var mi in optionalInstanceMethods)
|
|
GenerateMethod (type, mi, false, null, false, false, true);
|
|
|
|
// Generate Extension Methods of required [Async] decorated methods (we already do optional)
|
|
foreach (var ami in requiredInstanceAsyncMethods) {
|
|
var minfo = new MemberInformation (this, this, ami, type, null, is_extension_method: true);
|
|
GenerateAsyncMethod (minfo, AsyncMethodKind.Plain);
|
|
|
|
// Generate the overload with the out parameter
|
|
if (minfo.method.ReturnType != TypeManager.System_Void)
|
|
GenerateAsyncMethod (minfo, AsyncMethodKind.WithResultOutParameter);
|
|
}
|
|
|
|
// C# does not support extension properties, so create Get* and Set* accessors instead.
|
|
foreach (var pi in optionalInstanceProperties) {
|
|
var attrib = GetExportAttribute (pi);
|
|
var getter = pi.GetGetMethod ();
|
|
if (getter != null) {
|
|
PrintAttributes (pi, preserve:true, advice:true);
|
|
var ba = GetBindAttribute (getter);
|
|
GenerateMethod (type, getter, false, null, false, false, true, ba?.Selector ?? attrib.ToGetter (pi).Selector);
|
|
}
|
|
var setter = pi.GetSetMethod ();
|
|
if (setter != null) {
|
|
PrintAttributes (pi, preserve:true, advice:true);
|
|
var ba = GetBindAttribute (setter);
|
|
GenerateMethod (type, setter, false, null, false, false, true, ba?.Selector ?? attrib.ToSetter (pi).Selector);
|
|
}
|
|
}
|
|
indent--;
|
|
print ("}");
|
|
print ("");
|
|
}
|
|
|
|
// Add API from base interfaces we also need to implement.
|
|
foreach (var iface in ifaces) {
|
|
requiredInstanceMethods.AddRange (SelectProtocolMethods (iface, @static: false, required: true));
|
|
requiredInstanceProperties.AddRange (SelectProtocolProperties (iface, @static: false, required: true));
|
|
}
|
|
|
|
PrintPreserveAttribute (type);
|
|
print ("internal sealed class {0}Wrapper : BaseWrapper, I{0} {{", TypeName);
|
|
indent++;
|
|
// ctor (IntPtr, bool)
|
|
print ("[Preserve (Conditional = true)]");
|
|
print ("public {0}Wrapper ({1} handle, bool owns)", TypeName, NativeHandleType);
|
|
print ("\t: base (handle, owns)");
|
|
print ("{");
|
|
print ("}");
|
|
print ("");
|
|
// Methods
|
|
// First find duplicates and select the best one. We use the selector to determine what's a duplicate.
|
|
var methodData = requiredInstanceMethods.Select ((v) => {
|
|
return new MethodData {
|
|
Method = v,
|
|
ExportAttribute = GetExportAttribute (v),
|
|
};
|
|
}).ToArray ();
|
|
var methodsGroupedBySelector = methodData.GroupBy ((v) => v.ExportAttribute.Selector);
|
|
var duplicateMethodsGroupedBySelector = methodsGroupedBySelector.Where ((v) => v.Count () > 1);
|
|
if (duplicateMethodsGroupedBySelector.Any ()) {
|
|
// Yes, there are multiple methods with the same selector. Now we must compute the signature for all of them.
|
|
var computeSignature = new Func<MethodInfo, string> ((MethodInfo minfo) => {
|
|
var sig = new StringBuilder ();
|
|
sig.Append (minfo.Name).Append (" (");
|
|
foreach (var param in minfo.GetParameters ())
|
|
sig.Append (param.ParameterType.FullName).Append (' ');
|
|
sig.Append (')');
|
|
return sig.ToString ();
|
|
});
|
|
foreach (var gr in duplicateMethodsGroupedBySelector)
|
|
foreach (var m in gr)
|
|
m.Signature = computeSignature (m.Method);
|
|
|
|
// Verify that all the duplicate methods have the same signature.
|
|
// If not, show an error, and if they all have the same signature, remove all but one from the list of methods to generate.
|
|
foreach (var gr in duplicateMethodsGroupedBySelector) {
|
|
var distinctMethodsBySignature = gr.GroupBy ((v) => v.Signature).Select ((v) => v.First ()).ToArray ();
|
|
if (distinctMethodsBySignature.Length > 1) {
|
|
exceptions.Add (ErrorHelper.CreateError (1069, type.FullName, gr.Key, distinctMethodsBySignature [0].Method.DeclaringType.FullName, distinctMethodsBySignature [1].Method.DeclaringType.FullName,
|
|
distinctMethodsBySignature [0].Method.ToString (), distinctMethodsBySignature [1].Method.ToString ()));
|
|
continue;
|
|
}
|
|
|
|
// Remove all but the first method from the list required instance methods
|
|
var exclude = gr.Skip (1).Select ((v) => v.Method).ToArray ();
|
|
requiredInstanceMethods.RemoveAll ((v) => exclude.Contains (v));
|
|
}
|
|
|
|
}
|
|
// Go generate code for the methods
|
|
foreach (var mi in requiredInstanceMethods) {
|
|
GenerateMethod (type, mi, false, null, false, true, isBaseWrapperProtocolMethod:true);
|
|
}
|
|
|
|
// Properties
|
|
// First find duplicates and select the best one. Here we use the property name to find duplicates.
|
|
var propertyDuplicates = requiredInstanceProperties.GroupBy ((v) => v.Name).Where ((v) => v.Count () > 1).ToArray ();
|
|
if (propertyDuplicates.Any ()) {
|
|
foreach (var gr in propertyDuplicates) {
|
|
// Check that all the selectors in the group are identical
|
|
var properties = gr.ToArray ();
|
|
var exportAttributes = new ExportAttribute [properties.Length];
|
|
PropertyInfo readwrite = null;
|
|
PropertyInfo @readonly = null;
|
|
PropertyInfo @writeonly = null;
|
|
for (var i = 0; i < properties.Length; i++) {
|
|
// Check that all the duplicates use the same selector, and if not show a warning.
|
|
exportAttributes [i] = GetExportAttribute (properties [i]);
|
|
if (i > 0 && exportAttributes [i].Selector != exportAttributes [0].Selector) {
|
|
ErrorHelper.Warning (1068, type.FullName, gr.Key, properties [0].DeclaringType.FullName, properties [i].DeclaringType.FullName,
|
|
properties [0].DeclaringType.Name, properties [0].Name, exportAttributes [0].Selector, properties [i].DeclaringType.Name, properties [i].Name, exportAttributes [i].Selector);
|
|
}
|
|
if (properties [i].CanRead && properties [i].CanWrite) {
|
|
readwrite = properties [i];
|
|
} else if (!properties [i].CanRead) {
|
|
writeonly = properties [i];
|
|
} else if (!properties [i].CanWrite) {
|
|
@readonly = properties [i];
|
|
}
|
|
}
|
|
|
|
// Check that all the duplicates have the same property type
|
|
var propertyTypes = properties.GroupBy ((v) => v.PropertyType.FullName).Select ((v) => v.First ()).ToArray ();
|
|
if (propertyTypes.Length > 1) {
|
|
exceptions.Add (ErrorHelper.CreateError (1070, type.FullName, gr.Key, propertyTypes [0].DeclaringType.FullName, propertyTypes [1].DeclaringType.FullName,
|
|
FormatPropertyInfo (propertyTypes [0]), FormatType (type, propertyTypes [0].PropertyType), FormatPropertyInfo (propertyTypes [1]), FormatType (type, propertyTypes [0].PropertyType)));
|
|
}
|
|
|
|
// Select the best match of the properties: prefer a read/write property if it exists, otherwise a readonly or writeonly property.
|
|
PropertyInfo bestMatch;
|
|
if (readwrite != null) {
|
|
bestMatch = readwrite;
|
|
} else if (@readonly != null && writeonly != null) {
|
|
exceptions.Add (ErrorHelper.CreateError (1067, type.FullName, gr.Key, @readonly.DeclaringType.FullName, writeonly.DeclaringType.FullName,
|
|
FormatPropertyInfo (@readonly), FormatPropertyInfo (writeonly)));
|
|
continue;
|
|
} else if (@readonly != null) {
|
|
bestMatch = @readonly;
|
|
} else if (writeonly != null) {
|
|
bestMatch = writeonly;
|
|
} else {
|
|
exceptions.Add (ErrorHelper.CreateError (89, properties [0]));
|
|
continue;
|
|
}
|
|
// Finally remove the properties we don't want to generate.
|
|
var exclude = properties.Where ((v) => v != bestMatch);
|
|
requiredInstanceProperties.RemoveAll ((v) => exclude.Contains (v));
|
|
}
|
|
}
|
|
foreach (var pi in requiredInstanceProperties) {
|
|
GenerateProperty (type, pi, null, false, true);
|
|
}
|
|
indent--;
|
|
print ("}");
|
|
|
|
if (type.Namespace != null) {
|
|
indent--;
|
|
print ("}");
|
|
}
|
|
}
|
|
|
|
class MethodData
|
|
{
|
|
public MethodInfo Method;
|
|
public ExportAttribute ExportAttribute;
|
|
public string Signature;
|
|
}
|
|
|
|
bool ConformToNSCoding (Type type)
|
|
{
|
|
foreach (var intf in type.GetInterfaces ()) {
|
|
if (intf.Name == "NSCoding" || intf.Name == "INSCoding")
|
|
return true;
|
|
}
|
|
// [BaseType (x)] might implement NSCoding... and this means we need the .ctor(NSCoder)
|
|
var attrs = AttributeManager.GetCustomAttributes<BaseTypeAttribute> (type);
|
|
if (attrs == null || attrs.Length == 0)
|
|
return false;
|
|
return ConformToNSCoding (attrs [0].BaseType);
|
|
}
|
|
|
|
StreamWriter GetOutputStream (string @namespace, string name)
|
|
{
|
|
var dir = basedir;
|
|
|
|
if (!string.IsNullOrEmpty (@namespace))
|
|
dir = Path.Combine (dir, @namespace);
|
|
|
|
var filename = Path.Combine (dir, name + ".g.cs");
|
|
var counter = 2;
|
|
while (generated_files.Contains (filename)) {
|
|
filename = Path.Combine (dir, name + counter.ToString () + ".g.cs");
|
|
counter++;
|
|
}
|
|
generated_files.Add (filename);
|
|
if (!Directory.Exists (dir))
|
|
Directory.CreateDirectory (dir);
|
|
|
|
return new StreamWriter (filename);
|
|
}
|
|
|
|
|
|
StreamWriter GetOutputStreamForType (Type type)
|
|
{
|
|
if (type.Namespace == null)
|
|
ErrorHelper.Warning (1103, type.FullName);
|
|
|
|
var tn = GetGeneratedTypeName (type);
|
|
if (type.IsGenericType)
|
|
tn = tn + "_" + type.GetGenericArguments ().Length.ToString ();
|
|
return GetOutputStream (type.Namespace, tn);
|
|
}
|
|
|
|
void WriteMarkDirtyIfDerived (StreamWriter sw, Type type)
|
|
{
|
|
if (type.Name == "CALayer" && "CoreAnimation" == type.Namespace) {
|
|
sw.WriteLine ("\t\t\tMarkDirtyIfDerived ();");
|
|
}
|
|
}
|
|
|
|
public void PrintXpcInterfaceAttribute (ICustomAttributeProvider mi)
|
|
{
|
|
if (!AttributeManager.HasAttribute<XpcInterfaceAttribute> (mi))
|
|
return;
|
|
|
|
print ("[XpcInterface]");
|
|
}
|
|
|
|
// Function to check if PreserveAttribute is present and
|
|
// generate/print the same attribute as in bindings
|
|
public void PrintPreserveAttribute (ICustomAttributeProvider mi)
|
|
{
|
|
var p = AttributeManager.GetCustomAttribute<PreserveAttribute> (mi);
|
|
if (p == null)
|
|
return;
|
|
|
|
if (p.AllMembers)
|
|
print ("[Preserve (AllMembers = true)]");
|
|
else if (p.Conditional)
|
|
print ("[Preserve (Conditional = true)]");
|
|
else
|
|
print ("[Preserve]");
|
|
}
|
|
|
|
// Function to check if PrintAdviceAttribute is present and
|
|
// generate/print the same attribute as in bindings
|
|
public void PrintAdviceAttribute (ICustomAttributeProvider mi)
|
|
{
|
|
var p = AttributeManager.GetCustomAttribute<AdviceAttribute> (mi);
|
|
if (p == null)
|
|
return;
|
|
|
|
print ($"[Advice ({Quote (p.Message)})]");
|
|
}
|
|
|
|
public void PrintRequiresSuperAttribute (ICustomAttributeProvider mi)
|
|
{
|
|
var p = AttributeManager.GetCustomAttribute<RequiresSuperAttribute> (mi);
|
|
if (p == null)
|
|
return;
|
|
|
|
print ("[RequiresSuper]");
|
|
}
|
|
|
|
public void PrintNotImplementedAttribute (ICustomAttributeProvider mi)
|
|
{
|
|
var p = AttributeManager.GetCustomAttribute<NotImplementedAttribute> (mi);
|
|
if (p == null)
|
|
return;
|
|
|
|
print ($"[NotImplemented ({Quote (p.Message)})]");
|
|
}
|
|
|
|
public void PrintBindAsAttribute (ICustomAttributeProvider mi, StringBuilder sb = null)
|
|
{
|
|
var p = GetBindAsAttribute (mi);
|
|
if (p == null)
|
|
return;
|
|
|
|
var property = mi as PropertyInfo;
|
|
var method = mi as MethodInfo;
|
|
var param = mi as ParameterInfo;
|
|
var originalType = method?.ReturnType ?? property?.PropertyType;
|
|
originalType = originalType ?? param?.ParameterType;
|
|
|
|
var declaringType = (mi as MemberInfo)?.DeclaringType ?? param.Member.DeclaringType;
|
|
var pReturn = method != null ? "return: " : string.Empty;
|
|
|
|
var attribstr = $"[{pReturn}BindAs (typeof ({FormatType (declaringType, p.Type)}), OriginalType = typeof ({FormatType (declaringType, originalType)}))]";
|
|
|
|
if (sb != null)
|
|
sb.Append ($"{attribstr} ");
|
|
else
|
|
print (attribstr);
|
|
}
|
|
|
|
public void PrintAttributes (ICustomAttributeProvider mi, bool platform = false, bool preserve = false, bool advice = false, bool notImplemented = false, bool bindAs = false, bool requiresSuper = false)
|
|
{
|
|
if (platform)
|
|
PrintPlatformAttributes (mi as MemberInfo);
|
|
if (preserve)
|
|
PrintPreserveAttribute (mi);
|
|
if (advice)
|
|
PrintAdviceAttribute (mi);
|
|
if (notImplemented)
|
|
PrintNotImplementedAttribute (mi);
|
|
if (bindAs)
|
|
PrintBindAsAttribute (mi);
|
|
if (requiresSuper)
|
|
PrintRequiresSuperAttribute (mi);
|
|
}
|
|
|
|
public void ComputeLibraryName (FieldAttribute fieldAttr, Type type, string propertyName, out string library_name, out string library_path)
|
|
{
|
|
library_path = null;
|
|
|
|
if (fieldAttr != null && fieldAttr.LibraryName != null) {
|
|
// Remapped
|
|
library_name = fieldAttr.LibraryName;
|
|
if (library_name [0] == '+') {
|
|
switch (library_name) {
|
|
case "+CoreImage":
|
|
library_name = CoreImageMap;
|
|
break;
|
|
case "+CoreServices":
|
|
library_name = CoreServicesMap;
|
|
break;
|
|
case "+PDFKit":
|
|
library_name = "PdfKit";
|
|
library_path = PDFKitMap;
|
|
break;
|
|
}
|
|
} else {
|
|
// we get something in LibraryName from FieldAttribute so we asume
|
|
// it is a path to a library, so we save the path and change library name
|
|
// to a valid identifier if needed
|
|
library_path = library_name;
|
|
if (BindThirdPartyLibrary /* without extension makes more sense, but we can't change it since it breaks compat */) {
|
|
library_name = Path.GetFileName (library_name);
|
|
} else {
|
|
library_name = Path.GetFileNameWithoutExtension (library_name);
|
|
}
|
|
if (library_name.Contains ("."))
|
|
library_name = library_name.Replace (".", string.Empty);
|
|
}
|
|
} else if (BindThirdPartyLibrary) {
|
|
// User should provide a LibraryName
|
|
throw ErrorHelper.CreateError (1042, /* Missing '[Field (LibraryName=value)]' for {0} (e.g."__Internal") */ type.FullName + "." + propertyName);
|
|
} else {
|
|
library_name = type.Namespace;
|
|
}
|
|
|
|
if (!libraries.ContainsKey (library_name))
|
|
libraries.Add (library_name, library_path);
|
|
}
|
|
|
|
public string GetSelector (MemberInfo mi)
|
|
{
|
|
object [] attr = AttributeManager.GetCustomAttributes<ExportAttribute> (mi);
|
|
if (attr.Length != 1){
|
|
attr = AttributeManager.GetCustomAttributes<BindAttribute> (mi);
|
|
if (attr.Length != 1) {
|
|
return null;
|
|
}
|
|
else {
|
|
BindAttribute ba = (BindAttribute) attr [0];
|
|
return ba.Selector;
|
|
}
|
|
} else {
|
|
ExportAttribute ea = (ExportAttribute) attr [0];
|
|
return ea.Selector;
|
|
}
|
|
}
|
|
|
|
string GetAssemblyName ()
|
|
{
|
|
if (BindThirdPartyLibrary)
|
|
return Path.GetFileNameWithoutExtension (BindingTouch.outfile);
|
|
|
|
switch (BindingTouch.TargetFramework.Platform) {
|
|
case ApplePlatform.iOS:
|
|
return BindingTouch.IsDotNet ? "Microsoft.iOS" : "Xamarin.iOS";
|
|
case ApplePlatform.MacOSX:
|
|
return BindingTouch.IsDotNet ? "Microsoft.macOS" : "Xamarin.Mac";
|
|
case ApplePlatform.TVOS:
|
|
return BindingTouch.IsDotNet ? "Microsoft.tvOS" : "Xamarin.TVOS";
|
|
case ApplePlatform.WatchOS:
|
|
return BindingTouch.IsDotNet ? "Microsoft.watchOS" :"Xamarin.WatchOS";
|
|
case ApplePlatform.MacCatalyst:
|
|
return "Microsoft.MacCatalyst";
|
|
default:
|
|
throw ErrorHelper.CreateError (1053, /* Internal error: unknown target framework '{0}'. */ BindingTouch.TargetFramework);
|
|
}
|
|
}
|
|
|
|
public void Generate (Type type)
|
|
{
|
|
if (ZeroCopyStrings) {
|
|
ErrorHelper.Warning (1027);
|
|
ZeroCopyStrings = false;
|
|
}
|
|
|
|
type_wants_zero_copy = AttributeManager.HasAttribute<ZeroCopyStringsAttribute> (type) || ZeroCopyStrings;
|
|
var tsa = AttributeManager.GetCustomAttribute<ThreadSafeAttribute> (type);
|
|
// if we're inside a special namespace then default is non-thread safe, otherwise default is thread safe
|
|
if (ns.UINamespaces.Contains (type.Namespace)) {
|
|
// Any type inside these namespaces requires, by default, a thread check
|
|
// unless it has a [ThreadSafe] or [ThreadSafe (true)] attribute
|
|
type_needs_thread_checks = tsa == null || !tsa.Safe;
|
|
} else {
|
|
// Any type outside these namespaces do NOT require a thread check
|
|
// unless it has a [ThreadSafe (false)] attribute
|
|
type_needs_thread_checks = tsa != null && !tsa.Safe;
|
|
}
|
|
|
|
string TypeName = GetGeneratedTypeName (type);
|
|
indent = 0;
|
|
var instance_fields_to_clear_on_dispose = new List<string> ();
|
|
var gtype = GeneratedTypes.Lookup (type);
|
|
var appearance_selectors = gtype.ImplementsAppearance ? gtype.AppearanceSelectors : null;
|
|
|
|
using (var sw = GetOutputStreamForType (type)) {
|
|
this.sw = sw;
|
|
bool is_category_class = AttributeManager.HasAttribute<CategoryAttribute> (type);
|
|
bool is_static_class = AttributeManager.HasAttribute<StaticAttribute> (type) || is_category_class;
|
|
bool is_partial = AttributeManager.HasAttribute<PartialAttribute> (type);
|
|
var model = AttributeManager.GetCustomAttribute<ModelAttribute> (type);
|
|
bool is_model = model != null;
|
|
var protocol = AttributeManager.GetCustomAttribute<ProtocolAttribute> (type);
|
|
bool is_protocol = protocol != null;
|
|
bool is_abstract = AttributeManager.HasAttribute<AbstractAttribute> (type);
|
|
bool is_sealed = AttributeManager.HasAttribute<SealedAttribute> (type);
|
|
string class_visibility = type.IsInternal (this) ? "internal" : "public";
|
|
|
|
var default_ctor_visibility = AttributeManager.GetCustomAttribute<DefaultCtorVisibilityAttribute> (type);
|
|
BaseTypeAttribute bta = ReflectionExtensions.GetBaseTypeAttribute (type, this);
|
|
Type base_type = bta != null ? bta.BaseType : TypeManager.System_Object;
|
|
string objc_type_name = bta != null ? (bta.Name != null ? bta.Name : TypeName) : TypeName;
|
|
string register_name = objc_type_name;
|
|
|
|
if (is_model) {
|
|
if (!string.IsNullOrEmpty (model.Name)) {
|
|
register_name = model.Name;
|
|
#if NET
|
|
} else {
|
|
// For .NET, we'll always generate the Objective-C name. If a user wants to use a different name,
|
|
// they can set the model name (so we'll enter the previous condition)
|
|
#else
|
|
} else if (model.AutoGeneratedName) {
|
|
#endif
|
|
register_name = Registrar.Registrar.SanitizeObjectiveCName (GetAssemblyName () + "__" + type.FullName);
|
|
}
|
|
}
|
|
Header (sw);
|
|
|
|
if (is_protocol) {
|
|
if (is_static_class)
|
|
throw new BindingException (1025, true, type.FullName);
|
|
if (is_model && base_type == TypeManager.System_Object)
|
|
ErrorHelper.Warning (1060, type.FullName);
|
|
|
|
GenerateProtocolTypes (type, class_visibility, TypeName, protocol.Name ?? objc_type_name, protocol);
|
|
}
|
|
|
|
if (!is_static_class && bta == null && is_protocol)
|
|
return;
|
|
|
|
if (type.Namespace != null) {
|
|
print ("namespace {0} {{", type.Namespace);
|
|
indent++;
|
|
}
|
|
|
|
bool core_image_filter = false;
|
|
string class_mod = null;
|
|
|
|
is_direct_binding_value = null;
|
|
is_direct_binding = null;
|
|
if (BindThirdPartyLibrary)
|
|
is_direct_binding_value = string.Format ("GetType ().Assembly == global::{0}.this_assembly", ns.Messaging);
|
|
if (is_static_class || is_category_class || is_partial) {
|
|
base_type = TypeManager.System_Object;
|
|
if (!is_partial)
|
|
class_mod = "static ";
|
|
} else {
|
|
if (is_protocol) {
|
|
var pName = !string.IsNullOrEmpty (protocol.Name) ? $"Name = \"{protocol.Name}\"" : string.Empty;
|
|
print ("[Protocol({0}{1}{2})]", pName, (!string.IsNullOrEmpty (pName) && protocol.IsInformal) ? ", " : string.Empty, protocol.IsInformal ? "IsInformal = true" : string.Empty);
|
|
}
|
|
core_image_filter = AttributeManager.HasAttribute<CoreImageFilterAttribute> (type);
|
|
if (!type.IsEnum && !core_image_filter) {
|
|
if (is_model || AttributeManager.HasAttribute<SyntheticAttribute> (type)) {
|
|
is_direct_binding = false;
|
|
is_direct_binding_value = "false";
|
|
}
|
|
print ("[Register(\"{0}\", {1})]", register_name, is_direct_binding == false ? "false" : "true");
|
|
}
|
|
if (is_abstract || need_abstract.ContainsKey (type)) {
|
|
// Don't mark [Abstract] classes as abstract in .NET, we might need to create instances of them at some point.
|
|
// This can happen if the OS gives an instance of a subclass, but that subclass is private (so we haven't bound it).
|
|
// In this case, the best managed type is this type, but it can't be abstract if we want to create an instance of it.
|
|
// Except that we declare models as abstract, because they're meant to be subclassed (and they're not wrapping a native type anyway).
|
|
#if NET
|
|
if (is_model)
|
|
class_mod = "abstract ";
|
|
#else
|
|
class_mod = "abstract ";
|
|
#endif
|
|
} else if (is_sealed)
|
|
class_mod = "sealed ";
|
|
}
|
|
|
|
if (is_sealed && is_direct_binding == null) {
|
|
is_direct_binding = true;
|
|
is_direct_binding_value = "true";
|
|
}
|
|
|
|
if (is_model){
|
|
if (is_category_class)
|
|
ErrorHelper.Show (new BindingException (1022, true));
|
|
print ("[Model]");
|
|
}
|
|
|
|
PrintAttributes (type, platform:true, preserve:true, advice:true);
|
|
|
|
if (type.IsEnum) {
|
|
GenerateEnum (type);
|
|
return;
|
|
}
|
|
|
|
if (core_image_filter) {
|
|
GenerateFilter (type);
|
|
return;
|
|
}
|
|
|
|
// Order of this list is such that the base type will be first,
|
|
// followed by the protocol interface, followed by any other
|
|
// interfaces in ascending order
|
|
var implements_list = new List<string> ();
|
|
|
|
foreach (var protocolType in type.GetInterfaces ()) {
|
|
if (!AttributeManager.HasAttribute<ProtocolAttribute> (protocolType)) {
|
|
if (protocolType.Name [0] != 'I')
|
|
continue;
|
|
|
|
string nonInterfaceName = protocolType.Name.Substring (1);
|
|
if (!types.Any (x => x.Name.Contains (nonInterfaceName)))
|
|
continue;
|
|
|
|
if (is_category_class)
|
|
continue;
|
|
// 1. MKUserLocation implements the MKAnnotation protocol
|
|
// 2. The MKAnnotation protocol has a required 'coordinate' getter, and an optional 'coordinate' setter.
|
|
// We've bound the former using an [Abstract] readonly Coordinate property, and the latter using a SetCoordinate method.
|
|
// 3. MKUserLocation has a readwrite 'coordinate' property, which we've bound as a readwrite Coordinate property.
|
|
// 4. If we make MKUserLocation implement the MKAnnotation protocol in the api bindings, then we'll
|
|
// inline all the MKAnnotation protocol members in MKUserLocation. This includes the SetCoordinate method,
|
|
// which causes problems, because it implements the 'setCoordinate:' selector, which the Coordinate property
|
|
// does as well, resulting in:
|
|
// error MM4119: Could not register the selector 'setCoordinate:' of the member 'MapKit.MKUserLocation.set_Coordinate' because the selector is already registered on the member 'SetCoordinate'.
|
|
//
|
|
// So we can't make the MKUserLocation implement the MKAnnotation protocol in the api definition (for now at least).
|
|
if (type.Name =="MKUserLocation" && protocolType.Name == "IMKAnnotation")
|
|
continue;
|
|
#if !NET
|
|
if (type.Name == "NSFontAssetRequest" || protocolType.Name == "INSProgressReporting")
|
|
continue;
|
|
#endif
|
|
|
|
ErrorHelper.Warning (1111, protocolType, type, nonInterfaceName);
|
|
continue;
|
|
}
|
|
|
|
// skip protocols that aren't available on the current platform
|
|
if (protocolType.IsUnavailable (this))
|
|
continue;
|
|
|
|
// A protocol this class implements. We need to implement the corresponding interface for the protocol.
|
|
string pname = protocolType.Name;
|
|
// the extra 'I' is only required for the bindings being built, if it comes from something already
|
|
// built (e.g. monotouch.dll) then the interface will alreadybe prefixed
|
|
if (protocolType.Assembly.GetName ().Name == "temp")
|
|
pname = "I" + pname;
|
|
var iface = FormatType (type, protocolType.Namespace, pname);
|
|
if (!implements_list.Contains (iface))
|
|
implements_list.Add (iface);
|
|
}
|
|
|
|
implements_list.Sort (StringComparer.Ordinal);
|
|
|
|
if (is_protocol)
|
|
implements_list.Insert (0, "I" + type.Name);
|
|
|
|
if (base_type != TypeManager.System_Object && TypeName != "NSObject" && !is_category_class)
|
|
implements_list.Insert (0, FormatType (type, base_type));
|
|
|
|
if (type.IsNested){
|
|
var nestedName = type.FullName.Substring (type.Namespace.Length+1);
|
|
var container = nestedName.Substring (0, nestedName.IndexOf ('+'));
|
|
|
|
print ("partial class {0} {{", container);
|
|
indent++;
|
|
}
|
|
|
|
var class_name = TypeName;
|
|
var where_list = string.Empty;
|
|
if (type.IsGenericType) {
|
|
class_name += "<";
|
|
var gargs = type.GetGenericArguments ();
|
|
for (int i = 0; i < gargs.Length; i++) {
|
|
if (i > 0)
|
|
class_name += ", ";
|
|
class_name += FormatType (type, gargs [i]);
|
|
|
|
where_list += "\n\t\twhere " + gargs [i].Name + " : ";
|
|
var constraints = gargs [i].GetGenericParameterConstraints ();
|
|
if (constraints.Length > 0) {
|
|
var comma = string.Empty;
|
|
if (IsProtocol (constraints [0])) {
|
|
where_list += "NSObject";
|
|
comma = ", ";
|
|
}
|
|
|
|
for (int c = 0; c < constraints.Length; c++) {
|
|
where_list += comma + FormatType (type, constraints [c]);
|
|
comma = ", ";
|
|
}
|
|
} else {
|
|
where_list += "NSObject";
|
|
}
|
|
}
|
|
class_name += ">";
|
|
if (where_list.Length > 0)
|
|
where_list += " ";
|
|
}
|
|
|
|
print ("{0} unsafe {1}partial class {2} {3} {4}{{",
|
|
class_visibility,
|
|
class_mod,
|
|
class_name,
|
|
implements_list.Count == 0 ? string.Empty : ": " + string.Join (", ", implements_list),
|
|
where_list);
|
|
|
|
indent++;
|
|
|
|
if (!is_model && !is_partial) {
|
|
foreach (var ea in selectors [type].OrderBy (s => s, StringComparer.Ordinal)) {
|
|
var selectorField = SelectorField (ea, true);
|
|
if (!InlineSelectors) {
|
|
selectorField = selectorField.Substring (0, selectorField.Length - 6 /* Handle */);
|
|
print_generated_code ();
|
|
print ("const string {0} = \"{1}\";", selectorField, ea);
|
|
print ("static readonly {2} {0} = Selector.GetHandle (\"{1}\");", SelectorField (ea), ea, NativeHandleType);
|
|
}
|
|
}
|
|
}
|
|
print ("");
|
|
|
|
// Regular bindings (those that are not-static) or categories need this
|
|
if (!(is_static_class || is_partial) || is_category_class){
|
|
if (is_category_class)
|
|
objc_type_name = FormatCategoryClassName (bta);
|
|
|
|
if (!is_model) {
|
|
print_generated_code ();
|
|
var is32BitNotSupported = Is64BitiOSOnly (type);
|
|
if (is32BitNotSupported) {
|
|
// potentially avoid a .cctor and extra, unusable code
|
|
print ("#if ARCH_32");
|
|
print ("#pragma warning disable {0}", is_static_class ? "169" : "649");
|
|
print ("static readonly {0} class_ptr;", NativeHandleType);
|
|
print ("#pragma warning restore {0}", is_static_class ? "169" : "649");
|
|
print ("#else");
|
|
}
|
|
print ("static readonly {1} class_ptr = Class.GetHandle (\"{0}\");", objc_type_name, NativeHandleType);
|
|
if (is32BitNotSupported)
|
|
print ("#endif");
|
|
print ("");
|
|
}
|
|
}
|
|
|
|
if (!is_static_class && !is_partial){
|
|
if (!is_model && !external) {
|
|
print ("public {1} {2} ClassHandle {{ get {{ return class_ptr; }} }}\n", objc_type_name, TypeName == "NSObject" ? "virtual" : "override", NativeHandleType);
|
|
}
|
|
|
|
var ctor_visibility = is_abstract ? "protected" : "public";
|
|
var disable_default_ctor = false;
|
|
if (default_ctor_visibility != null) {
|
|
switch (default_ctor_visibility.Visibility) {
|
|
case Visibility.Public:
|
|
break; // default
|
|
case Visibility.Internal:
|
|
ctor_visibility = "internal";
|
|
break;
|
|
case Visibility.Protected:
|
|
ctor_visibility = "protected";
|
|
break;
|
|
case Visibility.ProtectedInternal:
|
|
ctor_visibility = "protected internal";
|
|
break;
|
|
case Visibility.Private:
|
|
ctor_visibility = string.Empty;
|
|
break;
|
|
case Visibility.Disabled:
|
|
disable_default_ctor = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (TypeName != "NSObject"){
|
|
var initSelector = (InlineSelectors || BindThirdPartyLibrary) ? "Selector.GetHandle (\"init\")" : "Selector.Init";
|
|
var initWithCoderSelector = (InlineSelectors || BindThirdPartyLibrary) ? "Selector.GetHandle (\"initWithCoder:\")" : "Selector.InitWithCoder";
|
|
string v = class_mod == "abstract " ? "protected" : ctor_visibility;
|
|
var is32BitNotSupported = Is64BitiOSOnly (type);
|
|
if (external) {
|
|
if (!disable_default_ctor) {
|
|
GeneratedCode (sw, 2);
|
|
if (AttributeManager.HasAttribute<DesignatedDefaultCtorAttribute> (type))
|
|
sw.WriteLine ("\n\n[DesignatedInitializer]");
|
|
sw.WriteLine ("\t\t[EditorBrowsable (EditorBrowsableState.Advanced)]");
|
|
sw.WriteLine ("\t\t[Export (\"init\")]");
|
|
sw.WriteLine ("\t\t{0} {1} () : base (NSObjectFlag.Empty)", v, TypeName);
|
|
sw.WriteLine ("\t\t{");
|
|
if (is32BitNotSupported) {
|
|
sw.WriteLine ("\t\t#if ARCH_32");
|
|
sw.WriteLine ("\tthrow new PlatformNotSupportedException (\"This API is not supported on this version of iOS\");");
|
|
sw.WriteLine ("\t\t#else");
|
|
}
|
|
if (is_direct_binding_value != null)
|
|
sw.WriteLine ("\t\t\tIsDirectBinding = {0};", is_direct_binding_value);
|
|
if (debug)
|
|
sw.WriteLine ("\t\t\tConsole.WriteLine (\"{0}.ctor ()\");", TypeName);
|
|
sw.WriteLine ("\t\t\tInitializeHandle (global::{1}.IntPtr_objc_msgSend (this.Handle, global::ObjCRuntime.{0}), \"init\");", initSelector, ns.Messaging);
|
|
sw.WriteLine ("\t\t\t");
|
|
if (is32BitNotSupported)
|
|
sw.WriteLine ("\t\t#endif");
|
|
sw.WriteLine ("\t\t}");
|
|
}
|
|
} else {
|
|
if (!disable_default_ctor) {
|
|
GeneratedCode (sw, 2);
|
|
if (AttributeManager.HasAttribute<DesignatedDefaultCtorAttribute> (type))
|
|
sw.WriteLine ("\t\t[DesignatedInitializer]");
|
|
sw.WriteLine ("\t\t[EditorBrowsable (EditorBrowsableState.Advanced)]");
|
|
sw.WriteLine ("\t\t[Export (\"init\")]");
|
|
sw.WriteLine ("\t\t{0} {1} () : base (NSObjectFlag.Empty)", v, TypeName);
|
|
sw.WriteLine ("\t\t{");
|
|
if (is32BitNotSupported) {
|
|
sw.WriteLine ("\t\t#if ARCH_32");
|
|
sw.WriteLine ("\tthrow new PlatformNotSupportedException (\"This API is not supported on this version of iOS\");");
|
|
sw.WriteLine ("\t\t#else");
|
|
}
|
|
if (type_needs_thread_checks) {
|
|
sw.Write ("\t\t\t");
|
|
GenerateThreadCheck (sw);
|
|
}
|
|
var indentation = 3;
|
|
WriteIsDirectBindingCondition (sw, ref indentation, is_direct_binding, is_direct_binding_value,
|
|
() => string.Format ("InitializeHandle (global::{1}.IntPtr_objc_msgSend (this.Handle, global::ObjCRuntime.{0}), \"init\");", initSelector, ns.Messaging),
|
|
() => string.Format ("InitializeHandle (global::{1}.IntPtr_objc_msgSendSuper (this.SuperHandle, global::ObjCRuntime.{0}), \"init\");", initSelector, ns.Messaging));
|
|
|
|
WriteMarkDirtyIfDerived (sw, type);
|
|
if (is32BitNotSupported)
|
|
sw.WriteLine ("\t\t#endif");
|
|
sw.WriteLine ("\t\t}");
|
|
sw.WriteLine ();
|
|
}
|
|
var nscoding = ConformToNSCoding (type);
|
|
if (nscoding) {
|
|
GeneratedCode (sw, 2);
|
|
sw.WriteLine ("\t\t[DesignatedInitializer]");
|
|
sw.WriteLine ("\t\t[EditorBrowsable (EditorBrowsableState.Advanced)]");
|
|
sw.WriteLine ("\t\t[Export (\"initWithCoder:\")]");
|
|
sw.WriteLine ("\t\t{0} {1} (NSCoder coder) : base (NSObjectFlag.Empty)", v, TypeName);
|
|
sw.WriteLine ("\t\t{");
|
|
if (is32BitNotSupported) {
|
|
sw.WriteLine ("\t\t#if ARCH_32");
|
|
sw.WriteLine ("\tthrow new PlatformNotSupportedException (\"This API is not supported on this version of iOS\");");
|
|
sw.WriteLine ("\t\t#else");
|
|
}
|
|
if (nscoding) {
|
|
if (debug)
|
|
sw.WriteLine ("\t\t\tConsole.WriteLine (\"{0}.ctor (NSCoder)\");", TypeName);
|
|
if (type_needs_thread_checks) {
|
|
sw.Write ("\t\t\t");
|
|
GenerateThreadCheck (sw);
|
|
}
|
|
var indentation = 3;
|
|
WriteIsDirectBindingCondition (sw, ref indentation, is_direct_binding, is_direct_binding_value,
|
|
() => string.Format ("InitializeHandle (global::{1}.IntPtr_objc_msgSend_IntPtr (this.Handle, {0}, coder.Handle), \"initWithCoder:\");", initWithCoderSelector, ns.Messaging),
|
|
() => string.Format ("InitializeHandle (global::{1}.IntPtr_objc_msgSendSuper_IntPtr (this.SuperHandle, {0}, coder.Handle), \"initWithCoder:\");", initWithCoderSelector, ns.Messaging));
|
|
WriteMarkDirtyIfDerived (sw, type);
|
|
} else {
|
|
sw.WriteLine ("\t\t\tthrow new InvalidOperationException (\"Type does not conform to NSCoding\");");
|
|
}
|
|
if (is32BitNotSupported)
|
|
sw.WriteLine ("\t\t#endif");
|
|
sw.WriteLine ("\t\t}");
|
|
sw.WriteLine ();
|
|
}
|
|
}
|
|
if (!is_sealed) {
|
|
GeneratedCode (sw, 2);
|
|
sw.WriteLine ("\t\t[EditorBrowsable (EditorBrowsableState.Advanced)]");
|
|
sw.WriteLine ("\t\tprotected {0} (NSObjectFlag t) : base (t)", TypeName);
|
|
sw.WriteLine ("\t\t{");
|
|
if (is_direct_binding_value != null)
|
|
sw.WriteLine ("\t\t\tIsDirectBinding = {0};", is_direct_binding_value);
|
|
WriteMarkDirtyIfDerived (sw, type);
|
|
sw.WriteLine ("\t\t}");
|
|
sw.WriteLine ();
|
|
}
|
|
GeneratedCode (sw, 2);
|
|
sw.WriteLine ("\t\t[EditorBrowsable (EditorBrowsableState.Advanced)]");
|
|
sw.Write ($"\t\t");
|
|
if (!is_sealed)
|
|
sw.Write ("protected ");
|
|
sw.WriteLine ($"internal {TypeName} ({NativeHandleType} handle) : base (handle)");
|
|
sw.WriteLine ("\t\t{");
|
|
if (is_direct_binding_value != null)
|
|
sw.WriteLine ("\t\t\tIsDirectBinding = {0};", is_direct_binding_value);
|
|
WriteMarkDirtyIfDerived (sw, type);
|
|
sw.WriteLine ("\t\t}");
|
|
sw.WriteLine ();
|
|
}
|
|
}
|
|
|
|
var bound_methods = new HashSet<MemberInformation> (); // List of methods bound on the class itself (not via protocols)
|
|
var generated_methods = new List<MemberInformation> (); // All method that have been generated
|
|
foreach (var mi in GetTypeContractMethods (type).OrderByDescending (m => m.Name == "Constructor").ThenBy (m => m.Name, StringComparer.Ordinal)) {
|
|
if (mi.IsSpecialName || (mi.Name == "Constructor" && type != mi.DeclaringType))
|
|
continue;
|
|
|
|
if (mi.IsUnavailable (this))
|
|
continue;
|
|
|
|
if (appearance_selectors != null && AttributeManager.HasAttribute<AppearanceAttribute> (mi))
|
|
appearance_selectors.Add (mi);
|
|
|
|
var minfo = new MemberInformation (this, this, mi, type, is_category_class ? bta.BaseType : null, is_model: is_model);
|
|
// the type above is not the the we're generating, e.g. it would be `NSCopying` for `Copy(NSZone?)`
|
|
minfo.is_type_sealed = is_sealed;
|
|
|
|
if (type == mi.DeclaringType || type.IsSubclassOf (mi.DeclaringType)) {
|
|
// not an injected protocol method.
|
|
bound_methods.Add (minfo);
|
|
} else {
|
|
// don't inject a protocol method if the class already
|
|
// implements the same method.
|
|
if (bound_methods.Contains (minfo))
|
|
continue;
|
|
|
|
var protocolsThatHaveThisMethod = GetTypeContractMethods (type).Where (x => { var sel = GetSelector (x); return sel != null && sel == minfo.selector; } );
|
|
if (protocolsThatHaveThisMethod.Count () > 1) {
|
|
// If multiple protocols have this method and we haven't generated a copy yet
|
|
if (generated_methods.Any (x => x.selector == minfo.selector))
|
|
continue;
|
|
|
|
// Verify all of the versions have the same arguments / return value
|
|
// And just generate the first one (us)
|
|
var methodParams = mi.GetParameters ();
|
|
foreach (var duplicateMethod in protocolsThatHaveThisMethod) {
|
|
if (mi.ReturnType != duplicateMethod.ReturnType)
|
|
throw new BindingException (1038, true, mi.Name, type.Name);
|
|
|
|
if (methodParams.Length != duplicateMethod.GetParameters().Length)
|
|
throw new BindingException (1039, true, minfo.selector, type.Name, mi.GetParameters ().Length, duplicateMethod.GetParameters ().Length);
|
|
}
|
|
|
|
int i = 0;
|
|
foreach (var param in methodParams) {
|
|
foreach (var duplicateMethod in protocolsThatHaveThisMethod) {
|
|
var duplicateParam = duplicateMethod.GetParameters ()[i];
|
|
if (param.IsOut != duplicateParam.IsOut)
|
|
throw new BindingException (1040, true, minfo.selector, type.Name, i);
|
|
if (param.ParameterType != duplicateParam.ParameterType)
|
|
throw new BindingException (1041, true, minfo.selector, type.Name, i, param.ParameterType, duplicateParam.ParameterType);
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
generated_methods.Add (minfo);
|
|
GenerateMethod (minfo);
|
|
}
|
|
|
|
var field_exports = new List<PropertyInfo> ();
|
|
var notifications = new List<PropertyInfo> ();
|
|
var bound_properties = new List<string> (); // List of properties bound on the class itself (not via protocols)
|
|
var generated_properties = new List<string> (); // All properties that have been generated
|
|
|
|
foreach (var pi in GetTypeContractProperties (type).OrderBy (p => p.Name, StringComparer.Ordinal)) {
|
|
|
|
if (pi.IsUnavailable (this))
|
|
continue;
|
|
|
|
if (AttributeManager.HasAttribute<FieldAttribute> (pi)) {
|
|
field_exports.Add (pi);
|
|
|
|
if (AttributeManager.HasAttribute<NotificationAttribute> (pi))
|
|
notifications.Add (pi);
|
|
continue;
|
|
}
|
|
|
|
if (appearance_selectors != null && AttributeManager.HasAttribute<AppearanceAttribute> (pi))
|
|
appearance_selectors.Add (pi);
|
|
|
|
if (type == pi.DeclaringType || type.IsSubclassOf (pi.DeclaringType)) {
|
|
// not an injected protocol property.
|
|
bound_properties.Add (pi.Name);
|
|
} else {
|
|
// don't inject a protocol property if the class already
|
|
// implements the same property.
|
|
if (bound_properties.Contains (pi.Name))
|
|
continue;
|
|
|
|
var protocolsThatHaveThisProp = GetTypeContractProperties (type).Where (x => x.Name == pi.Name);
|
|
if (protocolsThatHaveThisProp.Count () > 1) {
|
|
// If multiple protocols have this property and we haven't generated a copy yet
|
|
if (generated_properties.Contains (pi.Name))
|
|
continue;
|
|
|
|
// If there is a version that has get and set
|
|
if (protocolsThatHaveThisProp.Any (x => x.CanRead && x.CanWrite)) {
|
|
// Skip if we are not it
|
|
if (!(pi.CanRead && pi.CanWrite))
|
|
continue;
|
|
}
|
|
else {
|
|
// Verify all of the versions have the same get/set abilities since there is no universal get/set version
|
|
// And just generate the first one (us)
|
|
if (!protocolsThatHaveThisProp.All (x => x.CanRead == pi.CanRead && x.CanWrite == pi.CanWrite))
|
|
throw new BindingException (1037, true, pi.Name, type.Name);
|
|
}
|
|
}
|
|
}
|
|
|
|
generated_properties.Add (pi.Name);
|
|
GenerateProperty (type, pi, instance_fields_to_clear_on_dispose, is_model);
|
|
}
|
|
|
|
if (field_exports.Count != 0){
|
|
foreach (var field_pi in field_exports.OrderBy (f => f.Name, StringComparer.Ordinal)) {
|
|
var fieldAttr = AttributeManager.GetCustomAttribute<FieldAttribute> (field_pi);
|
|
ComputeLibraryName (fieldAttr, type, field_pi.Name, out string library_name, out string library_path);
|
|
|
|
bool is_unified_internal = field_pi.IsUnifiedInternal (this);
|
|
|
|
string fieldTypeName;
|
|
string smartEnumTypeName = null;
|
|
if (IsSmartEnum (field_pi.PropertyType)) {
|
|
fieldTypeName = FormatType (TypeManager.NSString.DeclaringType, TypeManager.NSString);
|
|
smartEnumTypeName = FormatType (field_pi.DeclaringType, field_pi.PropertyType);
|
|
} else
|
|
fieldTypeName = FormatType (field_pi.DeclaringType, field_pi.PropertyType);
|
|
|
|
bool nullable = false;
|
|
// Value types we dont cache for now, to avoid Nullable<T>
|
|
if (!field_pi.PropertyType.IsValueType || smartEnumTypeName != null) {
|
|
print_generated_code ();
|
|
PrintPreserveAttribute (field_pi);
|
|
print ("static {0}? _{1};", fieldTypeName, field_pi.Name);
|
|
}
|
|
|
|
PrintAttributes (field_pi, preserve:true, advice:true);
|
|
print ("[Field (\"{0}\", \"{1}\")]", fieldAttr.SymbolName, library_path ?? library_name);
|
|
PrintPlatformAttributes (field_pi);
|
|
if (AttributeManager.HasAttribute<AdvancedAttribute> (field_pi)) {
|
|
print ("[EditorBrowsable (EditorBrowsableState.Advanced)]");
|
|
}
|
|
// Check if this is a notification and print Advice to use our Notification API
|
|
if (AttributeManager.HasAttribute<NotificationAttribute> (field_pi))
|
|
print ($"[Advice (\"Use {type.Name}.Notifications.Observe{GetNotificationName (field_pi)} helper method instead.\")]");
|
|
|
|
print ("{0} static {1}{2} {3}{4} {{", field_pi.IsInternal (this) ? "internal" : "public",
|
|
smartEnumTypeName ?? fieldTypeName,
|
|
nullable ? "?" : "",
|
|
field_pi.Name,
|
|
is_unified_internal ? "_" : "");
|
|
indent++;
|
|
|
|
PrintAttributes (field_pi, platform:true);
|
|
PrintAttributes (field_pi.GetGetMethod (), preserve:true, advice:true);
|
|
print ("get {");
|
|
indent++;
|
|
if (field_pi.PropertyType == TypeManager.NSString){
|
|
print ("if (_{0} is null)", field_pi.Name);
|
|
indent++;
|
|
print ("_{0} = Dlfcn.GetStringConstant (Libraries.{2}.Handle, \"{1}\")!;", field_pi.Name, fieldAttr.SymbolName, library_name);
|
|
indent--;
|
|
print ("return _{0};", field_pi.Name);
|
|
} else if (field_pi.PropertyType.Name == "NSArray"){
|
|
print ("if (_{0} is null)", field_pi.Name);
|
|
indent++;
|
|
print ("_{0} = Runtime.GetNSObject<NSArray> (Dlfcn.GetIndirect (Libraries.{2}.Handle, \"{1}\"))!;", field_pi.Name, fieldAttr.SymbolName, library_name);
|
|
indent--;
|
|
print ("return _{0};", field_pi.Name);
|
|
} else if (field_pi.PropertyType.Name == "UTType") {
|
|
print ("if (_{0} is null)", field_pi.Name);
|
|
indent++;
|
|
print ("_{0} = Runtime.GetNSObject<UTType> (Dlfcn.GetIntPtr (Libraries.{2}.Handle, \"{1}\"))!;", field_pi.Name, fieldAttr.SymbolName, library_name);
|
|
indent--;
|
|
print ("return _{0};", field_pi.Name);
|
|
} else if (field_pi.PropertyType == TypeManager.System_Int32){
|
|
print ("return Dlfcn.GetInt32 (Libraries.{2}.Handle, \"{1}\");", field_pi.Name, fieldAttr.SymbolName, library_name);
|
|
} else if (field_pi.PropertyType == TypeManager.System_UInt32) {
|
|
print ("return Dlfcn.GetUInt32 (Libraries.{2}.Handle, \"{1}\");", field_pi.Name, fieldAttr.SymbolName, library_name);
|
|
} else if (field_pi.PropertyType == TypeManager.System_Double){
|
|
print ("return Dlfcn.GetDouble (Libraries.{2}.Handle, \"{1}\");", field_pi.Name, fieldAttr.SymbolName, library_name);
|
|
} else if (field_pi.PropertyType == TypeManager.System_Float){
|
|
print ("return Dlfcn.GetFloat (Libraries.{2}.Handle, \"{1}\");", field_pi.Name, fieldAttr.SymbolName, library_name);
|
|
} else if (field_pi.PropertyType == TypeManager.System_IntPtr){
|
|
print ("return Dlfcn.GetIntPtr (Libraries.{2}.Handle, \"{1}\");", field_pi.Name, fieldAttr.SymbolName, library_name);
|
|
} else if (field_pi.PropertyType.FullName == "System.Drawing.SizeF"){
|
|
print ("return Dlfcn.GetSizeF (Libraries.{2}.Handle, \"{1}\");", field_pi.Name, fieldAttr.SymbolName, library_name);
|
|
} else if (field_pi.PropertyType == TypeManager.System_Int64){
|
|
print ("return Dlfcn.GetInt64 (Libraries.{2}.Handle, \"{1}\");", field_pi.Name, fieldAttr.SymbolName, library_name);
|
|
} else if (field_pi.PropertyType == TypeManager.System_UInt64) {
|
|
print ("return Dlfcn.GetUInt64 (Libraries.{2}.Handle, \"{1}\");", field_pi.Name, fieldAttr.SymbolName, library_name);
|
|
} else
|
|
//
|
|
// Handle various blittable value types here
|
|
//
|
|
if (Frameworks.HaveCoreMedia && Frameworks.HaveAVFoundation && (field_pi.PropertyType == TypeManager.CMTime ||
|
|
field_pi.PropertyType == TypeManager.AVCaptureWhiteBalanceGains)) {
|
|
print ("return *(({3} *) Dlfcn.dlsym (Libraries.{2}.Handle, \"{1}\"));", field_pi.Name, fieldAttr.SymbolName, library_name,
|
|
FormatType (type, field_pi.PropertyType.Namespace, field_pi.PropertyType.Name));
|
|
} else if (field_pi.PropertyType == TypeManager.System_nint) {
|
|
print ("return Dlfcn.GetNInt (Libraries.{2}.Handle, \"{1}\");", field_pi.Name, fieldAttr.SymbolName, library_name);
|
|
} else if (field_pi.PropertyType == TypeManager.System_nuint) {
|
|
print ("return Dlfcn.GetNUInt (Libraries.{2}.Handle, \"{1}\");", field_pi.Name, fieldAttr.SymbolName, library_name);
|
|
} else if (field_pi.PropertyType == TypeManager.System_nfloat) {
|
|
print ("return Dlfcn.GetNFloat (Libraries.{2}.Handle, \"{1}\");", field_pi.Name, fieldAttr.SymbolName, library_name);
|
|
} else if (field_pi.PropertyType == TypeManager.CoreGraphics_CGSize){
|
|
print ("return Dlfcn.GetCGSize (Libraries.{2}.Handle, \"{1}\");", field_pi.Name, fieldAttr.SymbolName, library_name);
|
|
} else if (field_pi.PropertyType.IsEnum) {
|
|
var btype = field_pi.PropertyType.GetEnumUnderlyingType ();
|
|
if (smartEnumTypeName != null) {
|
|
print ("if (_{0} is null)", field_pi.Name);
|
|
indent++;
|
|
print ("_{0} = Dlfcn.GetStringConstant (Libraries.{2}.Handle, \"{1}\")!;", field_pi.Name, fieldAttr.SymbolName, library_name);
|
|
indent--;
|
|
print ($"return {smartEnumTypeName}Extensions.GetValue (_{field_pi.Name});");
|
|
} else if (GetNativeEnumToManagedExpression (field_pi.PropertyType, out var preExpression, out var postExpression, out var _)) {
|
|
if (btype == TypeManager.System_nint || btype == TypeManager.System_Int64)
|
|
print ($"return {preExpression}Dlfcn.GetNInt (Libraries.{library_name}.Handle, \"{fieldAttr.SymbolName}\"){postExpression};" );
|
|
else if (btype == TypeManager.System_nuint || btype == TypeManager.System_UInt64)
|
|
print ($"return {preExpression}Dlfcn.GetNUInt (Libraries.{library_name}.Handle, \"{fieldAttr.SymbolName}\"){postExpression};");
|
|
else
|
|
throw new BindingException (1014, true, fieldTypeName, FormatPropertyInfo (field_pi));
|
|
} else {
|
|
if (btype == TypeManager.System_Int32)
|
|
print ($"return ({fieldTypeName}) Dlfcn.GetInt32 (Libraries.{library_name}.Handle, \"{fieldAttr.SymbolName}\");");
|
|
else if (btype == TypeManager.System_UInt32)
|
|
print ($"return ({fieldTypeName}) Dlfcn.GetUInt32 (Libraries.{library_name}.Handle, \"{fieldAttr.SymbolName}\");");
|
|
else if (btype == TypeManager.System_Int64)
|
|
print ($"return ({fieldTypeName}) Dlfcn.GetInt64 (Libraries.{library_name}.Handle, \"{fieldAttr.SymbolName}\");");
|
|
else if (btype == TypeManager.System_UInt64)
|
|
print ($"return ({fieldTypeName}) Dlfcn.GetUInt64 (Libraries.{library_name}.Handle, \"{fieldAttr.SymbolName}\");");
|
|
else
|
|
throw new BindingException (1014, true, fieldTypeName, FormatPropertyInfo (field_pi));
|
|
}
|
|
} else {
|
|
if (field_pi.PropertyType == TypeManager.System_String)
|
|
throw new BindingException (1013, true);
|
|
else
|
|
throw new BindingException (1014, true, fieldTypeName, FormatPropertyInfo (field_pi));
|
|
}
|
|
|
|
indent--;
|
|
print ("}");
|
|
|
|
if (field_pi.CanWrite) {
|
|
PrintAttributes (field_pi, platform:true);
|
|
PrintAttributes (field_pi.GetSetMethod (), preserve:true, advice:true);
|
|
print ("set {");
|
|
indent++;
|
|
if (field_pi.PropertyType == TypeManager.System_Int32) {
|
|
print ("Dlfcn.SetInt32 (Libraries.{2}.Handle, \"{1}\", value);", field_pi.Name, fieldAttr.SymbolName, library_name);
|
|
} else if (field_pi.PropertyType == TypeManager.System_UInt32) {
|
|
print ("Dlfcn.SetUInt32 (Libraries.{2}.Handle, \"{1}\", value);", field_pi.Name, fieldAttr.SymbolName, library_name);
|
|
} else if (field_pi.PropertyType == TypeManager.System_Double) {
|
|
print ("Dlfcn.SetDouble (Libraries.{2}.Handle, \"{1}\", value);", field_pi.Name, fieldAttr.SymbolName, library_name);
|
|
} else if (field_pi.PropertyType == TypeManager.System_Float) {
|
|
print ("Dlfcn.SetFloat (Libraries.{2}.Handle, \"{1}\", value);", field_pi.Name, fieldAttr.SymbolName, library_name);
|
|
} else if (field_pi.PropertyType == TypeManager.System_IntPtr) {
|
|
print ("Dlfcn.SetIntPtr (Libraries.{2}.Handle, \"{1}\", value);", field_pi.Name, fieldAttr.SymbolName, library_name);
|
|
} else if (field_pi.PropertyType.FullName == "System.Drawing.SizeF") {
|
|
print ("Dlfcn.SetSizeF (Libraries.{2}.Handle, \"{1}\", value);", field_pi.Name, fieldAttr.SymbolName, library_name);
|
|
} else if (field_pi.PropertyType == TypeManager.System_Int64) {
|
|
print ("Dlfcn.SetInt64 (Libraries.{2}.Handle, \"{1}\", value);", field_pi.Name, fieldAttr.SymbolName, library_name);
|
|
} else if (field_pi.PropertyType == TypeManager.System_UInt64) {
|
|
print ("Dlfcn.SetUInt64 (Libraries.{2}.Handle, \"{1}\", value);", field_pi.Name, fieldAttr.SymbolName, library_name);
|
|
} else if (field_pi.PropertyType == TypeManager.NSString){
|
|
print ("Dlfcn.SetString (Libraries.{2}.Handle, \"{1}\", value);", field_pi.Name, fieldAttr.SymbolName, library_name);
|
|
} else if (field_pi.PropertyType.Name == "NSArray"){
|
|
print ("Dlfcn.SetArray (Libraries.{2}.Handle, \"{1}\", value);", field_pi.Name, fieldAttr.SymbolName, library_name);
|
|
} else if (field_pi.PropertyType == TypeManager.System_nint) {
|
|
print ("Dlfcn.SetNInt (Libraries.{2}.Handle, \"{1}\", value);", field_pi.Name, fieldAttr.SymbolName, library_name);
|
|
} else if (field_pi.PropertyType == TypeManager.System_nuint) {
|
|
print ("Dlfcn.SetNUInt (Libraries.{2}.Handle, \"{1}\", value);", field_pi.Name, fieldAttr.SymbolName, library_name);
|
|
} else if (field_pi.PropertyType == TypeManager.System_nfloat) {
|
|
print ("Dlfcn.SetNFloat (Libraries.{2}.Handle, \"{1}\", value);", field_pi.Name, fieldAttr.SymbolName, library_name);
|
|
} else if (field_pi.PropertyType == TypeManager.CoreGraphics_CGSize) {
|
|
print ("Dlfcn.SetCGSize (Libraries.{2}.Handle, \"{1}\", value);", field_pi.Name, fieldAttr.SymbolName, library_name);
|
|
} else if (field_pi.PropertyType.IsEnum) {
|
|
var btype = field_pi.PropertyType.GetEnumUnderlyingType ();
|
|
if (smartEnumTypeName != null)
|
|
print ($"Dlfcn.SetString (Libraries.{library_name}.Handle, \"{fieldAttr.SymbolName}\", value.GetConstant ());");
|
|
else if (GetNativeEnumToNativeExpression (field_pi.PropertyType, out var preExpression, out var postExpression, out var _)) {
|
|
if (btype == TypeManager.System_nint || (BindThirdPartyLibrary && btype == TypeManager.System_Int64))
|
|
print ($"Dlfcn.SetNInt (Libraries.{library_name}.Handle, \"{fieldAttr.SymbolName}\", (nint) {preExpression}value{postExpression});");
|
|
else if (btype == TypeManager.System_nuint || (BindThirdPartyLibrary && btype == TypeManager.System_UInt64))
|
|
print ($"Dlfcn.SetNUInt (Libraries.{library_name}.Handle, \"{fieldAttr.SymbolName}\", (nuint) {preExpression}value{postExpression});");
|
|
else
|
|
throw new BindingException (1021, true, fieldTypeName, field_pi.DeclaringType.FullName, field_pi.Name);
|
|
} else {
|
|
if (btype == TypeManager.System_Int32)
|
|
print ($"Dlfcn.SetInt32 (Libraries.{library_name}.Handle, \"{fieldAttr.SymbolName}\", (int) value);");
|
|
else if (btype == TypeManager.System_UInt32)
|
|
print ($"Dlfcn.SetUInt32 (Libraries.{library_name}.Handle, \"{fieldAttr.SymbolName}\", (uint) value);");
|
|
else if (btype == TypeManager.System_Int64)
|
|
print ($"Dlfcn.SetInt64 (Libraries.{library_name}.Handle, \"{fieldAttr.SymbolName}\", (long) value);");
|
|
else if (btype == TypeManager.System_UInt64)
|
|
print ($"Dlfcn.SetUInt64 (Libraries.{library_name}.Handle, \"{fieldAttr.SymbolName}\", (ulong) value);");
|
|
else
|
|
throw new BindingException (1021, true, fieldTypeName, field_pi.DeclaringType.FullName, field_pi.Name);
|
|
}
|
|
} else
|
|
throw new BindingException (1021, true, fieldTypeName, field_pi.DeclaringType.FullName, field_pi.Name);
|
|
indent--;
|
|
print ("}");
|
|
}
|
|
indent--;
|
|
print ("}");
|
|
}
|
|
}
|
|
|
|
var eventArgTypes = new Dictionary<string,ParameterInfo[]> ();
|
|
|
|
if (bta != null && bta.Events != null){
|
|
if (bta.Delegates == null)
|
|
throw new BindingException (1015, true, type.FullName);
|
|
|
|
print ("//");
|
|
print ("// Events and properties from the delegate");
|
|
print ("//\n");
|
|
|
|
if (bta.Events.Length != bta.Delegates.Length)
|
|
throw new BindingException (1023, true, type.FullName);
|
|
|
|
int delidx = 0;
|
|
foreach (var dtype in bta.Events) {
|
|
string delName = bta.Delegates [delidx++];
|
|
delName = delName.StartsWith ("Weak", StringComparison.Ordinal) ? delName.Substring(4) : delName;
|
|
|
|
// Here's the problem:
|
|
// If you have two or more types in an inheritence structure in the binding that expose events
|
|
// they can fight over who's generated delegate gets used if you use the events at both level.
|
|
// e.g. NSComboxBox.SelectionChanged (on NSComboxBox) and NSComboBox.EditingEnded (on NSTextField)
|
|
// We solve this under Unified when the delegate is protocalized (and leave Classic how it always has been)
|
|
// To handle this case, we do two things:
|
|
// 1) We have to ensure that the same internal delegate is uses for base and derived events, so they
|
|
// aren't stepping on each other toes. This means we always instance up the leaf class's internal delegate
|
|
// 2) We have to have an some for of "inheritance" in the generated delegates, so the
|
|
// base class can use this derived type (and have the events it expects on it) We do this via inhertiance and
|
|
// use of the protocal interfaces.
|
|
// See xamcore/test/apitest/DerivedEventTest.cs for specific tests
|
|
|
|
// Does this delegate qualify for this treatment. We fall back on the old "wrong" codegen if not.
|
|
bool isProtocolizedEventBacked = IsProtocolizedEventBacked (delName, type);
|
|
|
|
// The name of the the generated virtual property that specifies the type of the leaf most internal delegate
|
|
string delegateTypePropertyName = GetDelegateTypePropertyName (delName);
|
|
|
|
// The name of the method to instance up said internal delegate
|
|
string delegateCreationMethodName = "CreateInternalEvent" + delName + "Type";
|
|
|
|
// If we're not the base class in our inheritance tree to define this delegate, we need to override instead
|
|
bool shouldOverride = HasParentWithSameNamedDelegate (bta, delName);
|
|
|
|
// The name of the protocol in question
|
|
string interfaceName = GenerateInterfaceTypeName (bta, delName, dtype.Name);
|
|
|
|
bool hasKeepRefUntil = bta.KeepRefUntil != null;
|
|
|
|
if (isProtocolizedEventBacked) {
|
|
// The generated virtual type property and creation virtual method
|
|
string generatedTypeOverrideType = shouldOverride ? "override" : "virtual";
|
|
print ("internal {0} Type {1}", generatedTypeOverrideType, delegateTypePropertyName);
|
|
print ("{");
|
|
print (" get {{ return typeof (_{0}); }}", dtype.Name);
|
|
print ("}\n");
|
|
|
|
print ("internal {0} _{1} {2} ({3})", generatedTypeOverrideType, interfaceName, delegateCreationMethodName, hasKeepRefUntil ? "object oref" : "");
|
|
print ("{");
|
|
print (" return (_{0})(new _{1}({2}));", interfaceName, dtype.Name, hasKeepRefUntil ? "oref" : "");
|
|
print ("}\n");
|
|
}
|
|
|
|
if (!hasKeepRefUntil)
|
|
print ("{0}_{1} Ensure{1} ()", isProtocolizedEventBacked ? "internal " : "", dtype.Name);
|
|
else {
|
|
print ("static System.Collections.ArrayList? instances;");
|
|
print ("{0}_{1} Ensure{1} (object oref)", isProtocolizedEventBacked ? "internal " : "", dtype.Name);
|
|
}
|
|
|
|
print ("{"); indent++;
|
|
|
|
if (isProtocolizedEventBacked) {
|
|
// If our delegate not null and it isn't the same type as our property
|
|
// - We're in one of two cases: The user += an Event and then assigned their own delegate or the inverse
|
|
// - One of them isn't being called anymore no matter what. Throw an exception.
|
|
if (!BindThirdPartyLibrary) {
|
|
print ("if (Weak{0} != null)", delName);
|
|
print ("\t{0}.EnsureEventAndDelegateAreNotMismatched (Weak{1}, {2});", ApplicationClassName, delName, delegateTypePropertyName);
|
|
}
|
|
|
|
print ("var del = {1} as _{0};", dtype.Name, delName);
|
|
|
|
print ("if (del is null){");
|
|
indent++;
|
|
if (!hasKeepRefUntil)
|
|
print ("del = (_{0}){1} ();", dtype.Name, delegateCreationMethodName);
|
|
else {
|
|
string oref = "new object[] {\"oref\"}";
|
|
print ("del = (_{0}){1} ({2});", dtype.Name, delegateCreationMethodName, oref);
|
|
print ("if (instances is null) instances = new System.Collections.ArrayList ();");
|
|
print ("if (!instances.Contains (this)) instances.Add (this);");
|
|
}
|
|
print ("{0} = (I{1})del;", delName, dtype.Name);
|
|
indent--;
|
|
print ("}");
|
|
print ("return del;");
|
|
}
|
|
else {
|
|
print ("var del = {0};", delName);
|
|
print ("if (del is null || (!(del is _{0}))){{", dtype.Name);
|
|
print ("\tdel = new _{0} ({1});", dtype.Name, bta.KeepRefUntil == null ? "" : "oref");
|
|
if (hasKeepRefUntil) {
|
|
print ("\tif (instances is null) instances = new System.Collections.ArrayList ();");
|
|
print ("\tif (!instances.Contains (this)) instances.Add (this);");
|
|
}
|
|
print ("\t{0} = del;", delName);
|
|
print ("}");
|
|
print ("return (_{0}) del;", dtype.Name);
|
|
}
|
|
indent--; print ("}\n");
|
|
|
|
var noDefaultValue = new List<MethodInfo> ();
|
|
|
|
print ("#pragma warning disable 672");
|
|
print ("[Register]");
|
|
if (isProtocolizedEventBacked)
|
|
print ("internal class _{0} : {1}I{2} {{ ", dtype.Name, shouldOverride ? "_" + interfaceName + ", " : "NSObject, ", dtype.Name);
|
|
else
|
|
print ("sealed class _{0} : {1} {{ ", dtype.Name, RenderType (dtype));
|
|
|
|
|
|
indent++;
|
|
if (hasKeepRefUntil){
|
|
print ("object reference;");
|
|
print ("public _{0} (object reference) {{ this.reference = reference; IsDirectBinding = false; }}\n", dtype.Name);
|
|
} else
|
|
print ("public _{0} () {{ IsDirectBinding = false; }}\n", dtype.Name);
|
|
|
|
|
|
string shouldOverrideDelegateString = isProtocolizedEventBacked ? "" : "override ";
|
|
|
|
string previous_miname = null;
|
|
int miname_count = 0;
|
|
foreach (var mi in dtype.GatherMethods (this).OrderBy (m => m.Name, StringComparer.Ordinal)) {
|
|
if (ShouldSkipEventGeneration (mi))
|
|
continue;
|
|
|
|
var pars = mi.GetParameters ();
|
|
int minPars = bta.Singleton ? 0 : 1;
|
|
|
|
if (AttributeManager.HasAttribute<NoDefaultValueAttribute> (mi))
|
|
noDefaultValue.Add (mi);
|
|
|
|
if (pars.Length < minPars)
|
|
throw new BindingException (1003, true, dtype.FullName, mi.Name);
|
|
|
|
var sender = pars.Length == 0 ? "this" : pars [0].Name;
|
|
|
|
var miname = PascalCase (mi.Name);
|
|
if (miname == previous_miname) {
|
|
// overloads, add a numbered suffix (it's internal)
|
|
previous_miname = miname;
|
|
miname += (++miname_count).ToString ();
|
|
} else
|
|
previous_miname = miname;
|
|
|
|
if (mi.ReturnType == TypeManager.System_Void){
|
|
if (bta.Singleton || mi.GetParameters ().Length == 1)
|
|
print ("internal EventHandler? {0};", miname);
|
|
else
|
|
print ("internal EventHandler<{0}>? {1};", GetEventArgName (mi), miname);
|
|
} else
|
|
print ("internal {0}? {1};", GetDelegateName (mi), miname);
|
|
|
|
print ("[Preserve (Conditional = true)]");
|
|
if (isProtocolizedEventBacked)
|
|
print ("[Export (\"{0}\")]", FindSelector (dtype, mi));
|
|
|
|
print ("public {0}{1} {2} ({3})", shouldOverrideDelegateString, RenderType (mi.ReturnType), mi.Name, RenderParameterDecl (pars));
|
|
print ("{"); indent++;
|
|
|
|
if (mi.Name == bta.KeepRefUntil)
|
|
print ("instances?.Remove (reference);");
|
|
|
|
if (mi.ReturnType == TypeManager.System_Void){
|
|
string eaname;
|
|
|
|
if (debug)
|
|
print ("Console.WriteLine (\"Method {0}.{1} invoked\");", dtype.Name, mi.Name);
|
|
if (pars.Length != minPars){
|
|
eaname = GetEventArgName (mi);
|
|
if (!generatedEvents.ContainsKey (eaname) && !eventArgTypes.ContainsKey (eaname)){
|
|
eventArgTypes.Add (eaname, pars);
|
|
generatedEvents.Add (eaname, pars);
|
|
}
|
|
} else
|
|
eaname = "<NOTREACHED>";
|
|
|
|
print ("var handler = {0};", PascalCase (miname));
|
|
print ("if (handler != null){");
|
|
indent++;
|
|
string eventArgs;
|
|
if (pars.Length == minPars)
|
|
eventArgs = "EventArgs.Empty";
|
|
else {
|
|
print ("var args = new {0} ({1});", eaname, RenderArgs (pars.Skip (minPars), true));
|
|
eventArgs = "args";
|
|
}
|
|
|
|
print ("handler ({0}, {1});", sender, eventArgs);
|
|
if (pars.Length != minPars && MustPullValuesBack (pars.Skip (minPars))){
|
|
foreach (var par in pars.Skip (minPars)){
|
|
if (!par.ParameterType.IsByRef)
|
|
continue;
|
|
|
|
print ("{0} = args.{1};", par.Name, GetPublicParameterName (par));
|
|
}
|
|
}
|
|
if (AttributeManager.HasAttribute<CheckDisposedAttribute> (mi)) {
|
|
var arg = RenderArgs (pars.Take (1));
|
|
print ("if ({0}.Handle == IntPtr.Zero)", arg);
|
|
print ("\tthrow new ObjectDisposedException (\"{0}\", \"The object was disposed on the event, you should not call Dispose() inside the handler\");", arg);
|
|
}
|
|
indent--;
|
|
print ("}");
|
|
} else {
|
|
var delname = GetDelegateName (mi);
|
|
|
|
if (!generatedDelegates.ContainsKey (delname) && !delegate_types.ContainsKey (delname)){
|
|
generatedDelegates.Add (delname, null);
|
|
delegate_types.Add (type.Namespace + "." + delname, mi);
|
|
}
|
|
if (debug)
|
|
print ("Console.WriteLine (\"Method {0}.{1} invoked\");", dtype.Name, mi.Name);
|
|
|
|
print ("var handler = {0};", PascalCase (miname));
|
|
print ("if (handler != null)");
|
|
print (" return handler ({0}{1});",
|
|
sender,
|
|
pars.Length == minPars ? "" : String.Format (", {0}", RenderArgs (pars.Skip (1))));
|
|
|
|
if (AttributeManager.HasAttribute<NoDefaultValueAttribute> (mi))
|
|
print ("throw new You_Should_Not_Call_base_In_This_Method ();");
|
|
else {
|
|
var def = GetDefaultValue (mi);
|
|
if ((def is string) && ((def as string) == "null") && mi.ReturnType.IsValueType)
|
|
print ("throw new Exception (\"No event handler has been added to the {0} event.\");", mi.Name);
|
|
else {
|
|
foreach (var j in pars){
|
|
if (j.ParameterType.IsByRef && j.IsOut){
|
|
print ("{0} = null;", j.Name.GetSafeParamName ());
|
|
}
|
|
}
|
|
|
|
if (mi.ReturnType == TypeManager.System_nint) {
|
|
print ("return ((nint) ({0}));", def);
|
|
} else if (mi.ReturnType == TypeManager.System_nuint) {
|
|
print ("return ((nuint) ({0}));", def);
|
|
} else {
|
|
print ("return {0}!;", def);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
indent--;
|
|
print ("}\n");
|
|
}
|
|
|
|
if (noDefaultValue.Count > 0) {
|
|
string selRespondsToSelector = "Selector.GetHandle (\"respondsToSelector:\")";
|
|
|
|
if (!InlineSelectors) {
|
|
foreach (var mi in noDefaultValue) {
|
|
var export = AttributeManager.GetCustomAttribute<ExportAttribute> (mi);
|
|
print ("static {2} sel{0}Handle = Selector.GetHandle (\"{1}\");", mi.Name, export.Selector, NativeHandleType);
|
|
}
|
|
print ("static {0} selRespondsToSelector = " + selRespondsToSelector + ";", NativeHandleType);
|
|
selRespondsToSelector = "selRespondsToSelector";
|
|
}
|
|
|
|
print ("[Preserve (Conditional = true)]");
|
|
print ("public override bool RespondsToSelector (Selector? sel)");
|
|
print ("{");
|
|
++indent;
|
|
print ("if (sel is null)");
|
|
++indent;
|
|
print ("return false;");
|
|
--indent;
|
|
print ("{0} selHandle = sel.Handle;", NativeHandleType);
|
|
foreach (var mi in noDefaultValue.OrderBy (m => m.Name, StringComparer.Ordinal)) {
|
|
if (InlineSelectors) {
|
|
var export = AttributeManager.GetCustomAttribute<ExportAttribute> (mi);
|
|
print ("if (selHandle.Equals (Selector.GetHandle (\"{0}\")))", export.Selector);
|
|
} else {
|
|
print ("if (selHandle.Equals (sel{0}Handle))", mi.Name);
|
|
}
|
|
++indent;
|
|
print ("return {0} != null;", PascalCase (mi.Name));
|
|
--indent;
|
|
}
|
|
print ("return global::" + ns.Messaging + ".bool_objc_msgSendSuper_IntPtr (SuperHandle, " + selRespondsToSelector + ", selHandle);");
|
|
--indent;
|
|
print ("}");
|
|
|
|
// Make sure we generate the required signature in Messaging only if needed
|
|
// bool_objc_msgSendSuper_IntPtr: for respondsToSelector:
|
|
if (!send_methods.ContainsKey ("bool_objc_msgSendSuper_IntPtr")) {
|
|
print (m, "[DllImport (LIBOBJC_DYLIB, EntryPoint=\"objc_msgSendSuper\")]");
|
|
print (m, "public extern static bool bool_objc_msgSendSuper_IntPtr (IntPtr receiever, IntPtr selector, IntPtr arg1);");
|
|
RegisterMethodName ("bool_objc_msgSendSuper_IntPtr");
|
|
}
|
|
}
|
|
|
|
indent--;
|
|
print ("}");
|
|
|
|
print ("#pragma warning restore 672");
|
|
}
|
|
print ("");
|
|
|
|
string prev_miname = null;
|
|
int minameCount = 0;
|
|
repeatedDelegateApiNames.Clear ();
|
|
// Now add the instance vars and event handlers
|
|
foreach (var dtype in bta.Events.OrderBy (d => d.Name, StringComparer.Ordinal)) {
|
|
foreach (var mi in dtype.GatherMethods (this).OrderBy (m => m.Name, StringComparer.Ordinal)) {
|
|
if (ShouldSkipEventGeneration (mi))
|
|
continue;
|
|
|
|
string ensureArg = bta.KeepRefUntil == null ? "" : "this";
|
|
|
|
var miname = PascalCase (mi.Name);
|
|
if (miname == prev_miname) {
|
|
// overloads, add a numbered suffix (it's internal)
|
|
prev_miname = miname;
|
|
miname += (++minameCount).ToString ();
|
|
} else
|
|
prev_miname = miname;
|
|
|
|
if (mi.ReturnType == TypeManager.System_Void){
|
|
foreach (var oa in AttributeManager.GetCustomAttributes<ObsoleteAttribute> (mi))
|
|
print ("[Obsolete (\"{0}\", {1})]", oa.Message, oa.IsError ? "true" : "false");
|
|
|
|
if (bta.Singleton && mi.GetParameters ().Length == 0 || mi.GetParameters ().Length == 1)
|
|
print ("public event EventHandler {0} {{", CamelCase (GetEventName (mi)));
|
|
else
|
|
print ("public event EventHandler<{0}> {1} {{", GetEventArgName (mi), CamelCase (GetEventName (mi)));
|
|
print ("\tadd {{ Ensure{0} ({1})!.{2} += value; }}", dtype.Name, ensureArg, miname);
|
|
print ("\tremove {{ Ensure{0} ({1})!.{2} -= value; }}", dtype.Name, ensureArg, miname);
|
|
print ("}\n");
|
|
} else {
|
|
print ("public {0}? {1} {{", GetDelegateName (mi), CamelCase (GetDelegateApiName (mi)));
|
|
print ("\tget {{ return Ensure{0} ({1})!.{2}; }}", dtype.Name, ensureArg, miname);
|
|
print ("\tset {{ Ensure{0} ({1})!.{2} = value; }}", dtype.Name, ensureArg, miname);
|
|
print ("}\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Do we need a dispose method?
|
|
//
|
|
if (!is_static_class){
|
|
var attrs = AttributeManager.GetCustomAttributes<DisposeAttribute> (type);
|
|
// historical note: unlike many attributes our `DisposeAttribute` has `AllowMultiple=true`
|
|
var has_dispose_attributes = attrs.Length > 0;
|
|
if (has_dispose_attributes || (instance_fields_to_clear_on_dispose.Count > 0)) {
|
|
// if there'a any [Dispose] attribute then they all must opt-in in order for the generated Dispose method to be optimizable
|
|
bool optimizable = !has_dispose_attributes || IsOptimizable (type);
|
|
print_generated_code (optimizable: optimizable);
|
|
print ("protected override void Dispose (bool disposing)");
|
|
print ("{");
|
|
indent++;
|
|
if (has_dispose_attributes) {
|
|
foreach (var da in attrs)
|
|
Inject (da);
|
|
}
|
|
|
|
print ("base.Dispose (disposing);");
|
|
|
|
if (instance_fields_to_clear_on_dispose.Count > 0) {
|
|
print ("if (Handle == IntPtr.Zero) {");
|
|
indent++;
|
|
foreach (var field in instance_fields_to_clear_on_dispose.OrderBy (f => f, StringComparer.Ordinal))
|
|
print ("{0} = null;", field);
|
|
indent--;
|
|
print ("}");
|
|
}
|
|
|
|
indent--;
|
|
print ("}");
|
|
}
|
|
}
|
|
|
|
//
|
|
// Appearance class
|
|
//
|
|
var gt = GeneratedTypes.Lookup (type);
|
|
if (gt.ImplementsAppearance){
|
|
var parent_implements_appearance = gt.Parent != null && gt.ParentGenerated.ImplementsAppearance;
|
|
string base_class;
|
|
|
|
if (parent_implements_appearance){
|
|
var parent = GetGeneratedTypeName (gt.Parent);
|
|
base_class = "global::" + gt.Parent.FullName + "." + parent + "Appearance";
|
|
} else
|
|
base_class = "UIAppearance";
|
|
|
|
string appearance_type_name = TypeName + "Appearance";
|
|
print ("public partial class {0} : {1} {{", appearance_type_name, base_class);
|
|
indent++;
|
|
print ("protected internal {0} (IntPtr handle) : base (handle) {{}}", appearance_type_name);
|
|
|
|
if (appearance_selectors != null){
|
|
var currently_ignored_fields = new List<string> ();
|
|
|
|
foreach (MemberInfo mi in appearance_selectors.OrderBy (m => m.Name, StringComparer.Ordinal)) {
|
|
if (mi is MethodInfo)
|
|
GenerateMethod (type, mi as MethodInfo,
|
|
is_model: false,
|
|
category_extension_type: is_category_class ? base_type : null,
|
|
is_appearance: true);
|
|
else
|
|
GenerateProperty (type, mi as PropertyInfo, currently_ignored_fields, false);
|
|
}
|
|
}
|
|
|
|
indent--;
|
|
print ("}\n");
|
|
print ("public static {0}{1} Appearance {{", parent_implements_appearance ? "new " : "", appearance_type_name);
|
|
indent++;
|
|
print ("get {{ return new {0} (global::{1}.IntPtr_objc_msgSend (class_ptr, {2})); }}", appearance_type_name, ns.Messaging, InlineSelectors ? "ObjCRuntime.Selector.GetHandle (\"appearance\")" : "UIAppearance.SelectorAppearance");
|
|
indent--;
|
|
print ("}\n");
|
|
print ("public static {0}{1} GetAppearance<T> () where T: {2} {{", parent_implements_appearance ? "new " : "", appearance_type_name, TypeName);
|
|
indent++;
|
|
print ("return new {0} (global::{1}.IntPtr_objc_msgSend (Class.GetHandle (typeof (T)), {2}));", appearance_type_name, ns.Messaging, InlineSelectors ? "ObjCRuntime.Selector.GetHandle (\"appearance\")" : "UIAppearance.SelectorAppearance");
|
|
indent--;
|
|
print ("}\n");
|
|
print ("public static {0}{1} AppearanceWhenContainedIn (params Type [] containers)", parent_implements_appearance ? "new " : "", appearance_type_name);
|
|
print ("{");
|
|
indent++;
|
|
print ("return new {0} (UIAppearance.GetAppearance (class_ptr, containers));", appearance_type_name);
|
|
indent--;
|
|
print ("}\n");
|
|
|
|
print ("public static {0}{1} GetAppearance (UITraitCollection traits) {{", parent_implements_appearance ? "new " : "", appearance_type_name);
|
|
indent++;
|
|
print ("return new {0} (UIAppearance.GetAppearance (class_ptr, traits));", appearance_type_name);
|
|
indent--;
|
|
print ("}\n");
|
|
|
|
print ("public static {0}{1} GetAppearance (UITraitCollection traits, params Type [] containers) {{", parent_implements_appearance ? "new " : "", appearance_type_name);
|
|
indent++;
|
|
print ("return new {0} (UIAppearance.GetAppearance (class_ptr, traits, containers));", appearance_type_name);
|
|
indent--;
|
|
print ("}\n");
|
|
|
|
print ("public static {0}{1} GetAppearance<T> (UITraitCollection traits) where T: {2} {{", parent_implements_appearance ? "new " : "", appearance_type_name, TypeName);
|
|
indent++;
|
|
print ("return new {0} (UIAppearance.GetAppearance (Class.GetHandle (typeof (T)), traits));", appearance_type_name);
|
|
indent--;
|
|
print ("}\n");
|
|
|
|
print ("public static {0}{1} GetAppearance<T> (UITraitCollection traits, params Type [] containers) where T: {2}{{", parent_implements_appearance ? "new " : "", appearance_type_name, TypeName);
|
|
indent++;
|
|
print ("return new {0} (UIAppearance.GetAppearance (Class.GetHandle (typeof (T)), containers));", appearance_type_name);
|
|
indent--;
|
|
print ("}\n");
|
|
|
|
print ("");
|
|
}
|
|
//
|
|
// Notification extensions
|
|
//
|
|
if (notifications.Count > 0){
|
|
print ("\n");
|
|
print ("//");
|
|
print ("// Notifications");
|
|
print ("//");
|
|
|
|
print ("public static partial class Notifications {\n");
|
|
foreach (var property in notifications.OrderBy (p => p.Name, StringComparer.Ordinal)) {
|
|
string notification_name = GetNotificationName (property);
|
|
string notification_center = GetNotificationCenter (property);
|
|
|
|
foreach (var notification_attribute in AttributeManager.GetCustomAttributes<NotificationAttribute> (property)) {
|
|
Type event_args_type = notification_attribute.Type;
|
|
string event_name = event_args_type == null ? "NSNotificationEventArgs" : event_args_type.FullName;
|
|
|
|
if (event_args_type != null)
|
|
notification_event_arg_types [event_args_type] = event_args_type;
|
|
print ("\tpublic static NSObject Observe{0} (EventHandler<{1}> handler)", notification_name, event_name);
|
|
print ("\t{");
|
|
print ("\t\treturn {0}.AddObserver ({1}, notification => handler (null, new {2} (notification)));", notification_center, property.Name, event_name);
|
|
print ("\t}");
|
|
print ("\tpublic static NSObject Observe{0} (NSObject objectToObserve, EventHandler<{1}> handler)", notification_name, event_name);
|
|
print ("\t{");
|
|
print ("\t\treturn {0}.AddObserver ({1}, notification => handler (null, new {2} (notification)), objectToObserve);", notification_center, property.Name, event_name);
|
|
print ("\t}");
|
|
}
|
|
}
|
|
print ("\n}");
|
|
}
|
|
|
|
indent--;
|
|
print ("}} /* class {0} */", TypeName);
|
|
|
|
//
|
|
// Copy delegates from the API files into the output if they were declared there
|
|
//
|
|
var rootAssembly = types [0].Assembly;
|
|
foreach (var deltype in trampolines.Keys.OrderBy (d => d.Name, StringComparer.Ordinal)) {
|
|
if (deltype.Assembly != rootAssembly)
|
|
continue;
|
|
|
|
// This formats the delegate
|
|
delegate_types [deltype.FullName] = deltype.GetMethod ("Invoke");
|
|
}
|
|
|
|
if (eventArgTypes.Count > 0){
|
|
print ("\n");
|
|
print ("//");
|
|
print ("// EventArgs classes");
|
|
print ("//");
|
|
}
|
|
// Now add the EventArgs classes
|
|
foreach (var eaclass in eventArgTypes.Keys.OrderBy (e => e, StringComparer.Ordinal)) {
|
|
if (skipGeneration.ContainsKey (eaclass)){
|
|
continue;
|
|
}
|
|
int minPars = bta.Singleton ? 0 : 1;
|
|
|
|
var pars = eventArgTypes [eaclass];
|
|
|
|
print ("public partial class {0} : EventArgs {{", eaclass); indent++;
|
|
print ("public {0} ({1})", eaclass, RenderParameterDecl (pars.Skip (1), true));
|
|
print ("{");
|
|
indent++;
|
|
foreach (var p in pars.Skip (minPars).OrderBy (p => p.Name, StringComparer.Ordinal)) {
|
|
print ("this.{0} = {1};", GetPublicParameterName (p), p.Name.GetSafeParamName ());
|
|
}
|
|
indent--;
|
|
print ("}");
|
|
|
|
// Now print the properties
|
|
foreach (var p in pars.Skip (minPars).OrderBy (p => p.Name, StringComparer.Ordinal)) {
|
|
var pt = p.ParameterType;
|
|
var bareType = pt.IsByRef ? pt.GetElementType () : pt;
|
|
var nullable = !pt.IsValueType && AttributeManager.HasAttribute<NullAllowedAttribute> (p);
|
|
|
|
print ("public {0}{1} {2} {{ get; set; }}", RenderType (bareType), nullable ? "?" : "", GetPublicParameterName (p));
|
|
}
|
|
indent--; print ("}\n");
|
|
}
|
|
|
|
if (async_result_types.Count > 0) {
|
|
print ("\n");
|
|
print ("//");
|
|
print ("// Async result classes");
|
|
print ("//");
|
|
}
|
|
|
|
foreach (var async_type in async_result_types.OrderBy (t => t.Item1, StringComparer.Ordinal)) {
|
|
if (async_result_types_emitted.Contains (async_type.Item1))
|
|
continue;
|
|
async_result_types_emitted.Add (async_type.Item1);
|
|
|
|
print ("public partial class {0} {{", async_type.Item1); indent++;
|
|
|
|
StringBuilder ctor = new StringBuilder ();
|
|
|
|
bool comma = false;
|
|
foreach (var pi in async_type.Item2) {
|
|
var safe_name = pi.Name.GetSafeParamName ();
|
|
print ("public {0} {1} {{ get; set; }}",
|
|
FormatType (type, pi.ParameterType),
|
|
Capitalize (safe_name));
|
|
|
|
if (comma)
|
|
ctor.Append (", ");
|
|
comma = true;
|
|
ctor.Append (FormatType (type, pi.ParameterType)).Append (" ").Append (safe_name);
|
|
}
|
|
|
|
print ("\npartial void Initialize ();");
|
|
|
|
print ("\npublic {0} ({1}) {{", async_type.Item1, ctor); indent++;
|
|
foreach (var pi in async_type.Item2) {
|
|
var safe_name = pi.Name.GetSafeParamName ();
|
|
print ("this.{0} = {1};", Capitalize (safe_name), safe_name);
|
|
}
|
|
print ("Initialize ();");
|
|
indent--; print ("}");
|
|
|
|
indent--; print ("}\n");
|
|
}
|
|
async_result_types.Clear ();
|
|
|
|
if (type.IsNested){
|
|
indent--;
|
|
print ("}");
|
|
}
|
|
if (type.Namespace != null) {
|
|
indent--;
|
|
print ("}");
|
|
}
|
|
}
|
|
}
|
|
|
|
static string GetDelegateTypePropertyName (string delName)
|
|
{
|
|
return "GetInternalEvent" + delName + "Type";
|
|
}
|
|
|
|
bool SafeIsProtocolizedEventBacked (string propertyName, Type type)
|
|
{
|
|
return CoreIsProtocolizedEventBacked (propertyName, type, false);
|
|
}
|
|
|
|
bool IsProtocolizedEventBacked (string propertyName, Type type)
|
|
{
|
|
return CoreIsProtocolizedEventBacked (propertyName, type, true);
|
|
}
|
|
|
|
bool CoreIsProtocolizedEventBacked (string propertyName, Type type, bool shouldThrowOnNotFound)
|
|
{
|
|
PropertyInfo pi = type.GetProperty(propertyName);
|
|
BaseTypeAttribute bta = ReflectionExtensions.GetBaseTypeAttribute (type, this);
|
|
if (pi == null || bta == null) {
|
|
if (shouldThrowOnNotFound) {
|
|
if (propertyName == "Delegate" && bta.Delegates.Count () > 0) {
|
|
var delegates = new List <string> (bta.Delegates);
|
|
// grab all the properties that have the Wrap attr
|
|
var propsAttrs = from p in type.GetProperties ()
|
|
let attrs = AttributeManager.GetCustomAttributes<WrapAttribute> (p)
|
|
where p.Name != "Delegate" && attrs.Length > 0
|
|
select new { Name = p.Name, Attributes = Array.ConvertAll (attrs, item => item.MethodName) };
|
|
|
|
var props = new List <string> ();
|
|
foreach (var p in propsAttrs) {
|
|
if (delegates.Intersect (p.Attributes).Any ()) {
|
|
// add quoates since we are only using this info for the exception message
|
|
props.Add (String.Format ("'{0}'", p.Name));
|
|
}
|
|
}
|
|
if (props.Count == 1)
|
|
throw new BindingException (1112, true,
|
|
props[0], false);
|
|
else if (props.Count > 1)
|
|
throw new BindingException (1112, true,
|
|
String.Join (", ", props.ToArray ()), false);
|
|
else
|
|
throw new BindingException (1113, true, false);
|
|
} else
|
|
throw new BindingException (1114, propertyName, type, false);
|
|
} else
|
|
return false;
|
|
}
|
|
|
|
return Protocolize (pi) && bta.Events != null && bta.Events.Any (x => x.Name == pi.PropertyType.Name);
|
|
}
|
|
|
|
string FindSelector (Type type, MethodInfo mi)
|
|
{
|
|
Type currentType = type;
|
|
do
|
|
{
|
|
// avoid AmbiguousMatchException when GetMethod is used.
|
|
var parameters = mi.GetParameters ().Select ((arg) => arg.ParameterType).ToArray ();
|
|
MethodInfo method = currentType.GetMethod (mi.Name, parameters);
|
|
if (method != null) {
|
|
string wrap;
|
|
ExportAttribute export = GetExportAttribute (method, out wrap);
|
|
if (export != null)
|
|
return export.Selector;
|
|
}
|
|
BaseTypeAttribute bta = ReflectionExtensions.GetBaseTypeAttribute (currentType, this);
|
|
|
|
if (bta == null)
|
|
break;
|
|
currentType = bta.BaseType;
|
|
}
|
|
while (currentType != null);
|
|
throw new BindingException (1076, true, mi, type);
|
|
}
|
|
|
|
string GenerateInterfaceTypeName (BaseTypeAttribute bta, string delName, string currentTypeName)
|
|
{
|
|
bool shouldOverride = HasParentWithSameNamedDelegate (bta, delName);
|
|
if (shouldOverride) {
|
|
Type parentType = GetParentTypeWithSameNamedDelegate (bta, delName);
|
|
PropertyInfo parentProperty = parentType.GetProperty (delName);
|
|
return parentProperty.PropertyType.Name;
|
|
}
|
|
return currentTypeName;
|
|
}
|
|
|
|
bool ShouldSkipEventGeneration (MethodInfo mi)
|
|
{
|
|
// Skip property getter/setters
|
|
if (mi.IsSpecialName && (mi.Name.StartsWith ("get_", StringComparison.Ordinal) || mi.Name.StartsWith ("set_", StringComparison.Ordinal)))
|
|
return true;
|
|
|
|
if (mi.IsUnavailable (this))
|
|
return true;
|
|
|
|
// Skip those methods marked to be ignored by the developer
|
|
return AttributeManager.HasAttribute<IgnoredInDelegateAttribute> (mi);
|
|
}
|
|
|
|
// Safely strips away any Weak from the beginning of either delegate and returns if they match
|
|
static bool CompareTwoDelegateNames (string lhsDel, string rhsDel)
|
|
{
|
|
lhsDel = lhsDel.StartsWith ("Weak", StringComparison.Ordinal) ? lhsDel.Substring (4): lhsDel;
|
|
rhsDel = rhsDel.StartsWith ("Weak", StringComparison.Ordinal) ? rhsDel.Substring (4): rhsDel;
|
|
return lhsDel == rhsDel;
|
|
}
|
|
|
|
Type GetParentTypeWithSameNamedDelegate (BaseTypeAttribute bta, string delegateName)
|
|
{
|
|
Type currentType = bta.BaseType;
|
|
while (currentType != null && currentType != TypeManager.NSObject)
|
|
{
|
|
BaseTypeAttribute currentBta = ReflectionExtensions.GetBaseTypeAttribute (currentType, this);
|
|
if (currentBta != null && currentBta.Events != null) {
|
|
int delidx = 0;
|
|
foreach (var v in currentBta.Events) {
|
|
string currentDelName = currentBta.Delegates [delidx++];
|
|
if (CompareTwoDelegateNames (currentDelName, delegateName))
|
|
return currentType;
|
|
}
|
|
}
|
|
currentType = currentType.BaseType;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
bool HasParentWithSameNamedDelegate (BaseTypeAttribute bta, string delegateName)
|
|
{
|
|
return GetParentTypeWithSameNamedDelegate (bta, delegateName) != null;
|
|
}
|
|
|
|
// TODO: If we ever have an API with nested properties of the same name more than
|
|
// 2 deep, we'll need to have this return a list of PropertyInfo and comb through them all.
|
|
PropertyInfo GetParentTypeWithSameNamedProperty (BaseTypeAttribute bta, string propertyName)
|
|
{
|
|
if (bta == null)
|
|
return null;
|
|
|
|
Type currentType = bta.BaseType;
|
|
while (currentType != null && currentType != TypeManager.NSObject)
|
|
{
|
|
PropertyInfo prop = currentType.GetProperty (propertyName, BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
|
|
if (prop != null)
|
|
return prop;
|
|
currentType = currentType.BaseType;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
static string Capitalize (string str)
|
|
{
|
|
if (str.StartsWith ("@", StringComparison.Ordinal))
|
|
return char.ToUpper (str[1]) + str.Substring (2);
|
|
|
|
return char.ToUpper (str[0]) + str.Substring (1);
|
|
}
|
|
|
|
string GetNotificationCenter (PropertyInfo pi)
|
|
{
|
|
var a = AttributeManager.GetCustomAttributes<NotificationAttribute> (pi);
|
|
return a [0].NotificationCenter ?? "NSNotificationCenter.DefaultCenter";
|
|
}
|
|
|
|
string GetNotificationName (PropertyInfo pi)
|
|
{
|
|
// TODO: fetch the NotificationAttribute, see if there is an override there.
|
|
var name = pi.Name;
|
|
if (name.EndsWith ("Notification", StringComparison.Ordinal))
|
|
return name.Substring (0, name.Length-"Notification".Length);
|
|
return name;
|
|
}
|
|
|
|
Type GetNotificationArgType (PropertyInfo pi)
|
|
{
|
|
return AttributeManager.GetCustomAttributes<NotificationAttribute> (pi) [0].Type;
|
|
}
|
|
|
|
//
|
|
// Support for the automatic delegate/event generation
|
|
//
|
|
string RenderParameterDecl (IEnumerable<ParameterInfo> pi)
|
|
{
|
|
return RenderParameterDecl (pi, false);
|
|
}
|
|
|
|
string RenderParameterDecl (IEnumerable<ParameterInfo> pi, bool removeRefTypes)
|
|
{
|
|
return String.Join (", ", pi.Select (p=>RenderSingleParameter(p, removeRefTypes) + " " + p.Name.GetSafeParamName ()).ToArray ());
|
|
}
|
|
|
|
string RenderSingleParameter (ParameterInfo p, bool removeRefTypes)
|
|
{
|
|
var protocolized = Protocolize (p);
|
|
var prefix = protocolized ? "I" : "";
|
|
var pt = p.ParameterType;
|
|
|
|
string name;
|
|
if (pt.IsByRef) {
|
|
pt = pt.GetElementType ();
|
|
name = (removeRefTypes ? "" : (p.IsOut ? "out " : "ref ")) + prefix + RenderType (pt, p);
|
|
} else
|
|
name = prefix + RenderType (pt, p);
|
|
if (!pt.IsValueType && AttributeManager.HasAttribute<NullAllowedAttribute> (p))
|
|
name += "?";
|
|
return name;
|
|
}
|
|
|
|
string GetPublicParameterName (ParameterInfo pi)
|
|
{
|
|
var attrs = AttributeManager.GetCustomAttributes<EventNameAttribute> (pi);
|
|
if (attrs.Length == 0)
|
|
return CamelCase (pi.Name).GetSafeParamName ();
|
|
|
|
var a = attrs [0];
|
|
return CamelCase (a.EvtName).GetSafeParamName ();
|
|
}
|
|
|
|
string RenderArgs (IEnumerable<ParameterInfo> pi)
|
|
{
|
|
return RenderArgs (pi, false);
|
|
}
|
|
|
|
string RenderArgs (IEnumerable<ParameterInfo> pi, bool removeRefTypes)
|
|
{
|
|
return String.Join (", ", pi.Select (p => (p.ParameterType.IsByRef ? (removeRefTypes ? "" : (p.IsOut ? "out " : "ref ")) : "")+ p.Name.GetSafeParamName ()).ToArray ());
|
|
}
|
|
|
|
bool MustPullValuesBack (IEnumerable<ParameterInfo> parameters)
|
|
{
|
|
return parameters.Any (pi => pi.ParameterType.IsByRef);
|
|
}
|
|
|
|
string CamelCase (string ins)
|
|
{
|
|
return Char.ToUpper (ins [0]) + ins.Substring (1);
|
|
}
|
|
|
|
string PascalCase (string ins)
|
|
{
|
|
return Char.ToLower (ins [0]) + ins.Substring (1);
|
|
}
|
|
|
|
Dictionary<string,bool> skipGeneration = new Dictionary<string,bool> ();
|
|
string GetEventName (MethodInfo mi)
|
|
{
|
|
var a = AttributeManager.GetCustomAttribute<EventNameAttribute> (mi);
|
|
if (a == null)
|
|
return mi.Name;
|
|
var ea = (EventNameAttribute) a;
|
|
|
|
return ea.EvtName;
|
|
}
|
|
|
|
HashSet<string> repeatedDelegateApiNames = new HashSet<string> ();
|
|
string GetDelegateApiName (MethodInfo mi)
|
|
{
|
|
var a = AttributeManager.GetCustomAttribute<DelegateApiNameAttribute> (mi);
|
|
|
|
if (repeatedDelegateApiNames.Contains (mi.Name) && a == null)
|
|
throw new BindingException (1043, true, mi.Name);
|
|
if (a == null) {
|
|
repeatedDelegateApiNames.Add (mi.Name);
|
|
return mi.Name;
|
|
}
|
|
|
|
var apiName = (DelegateApiNameAttribute) a;
|
|
if (repeatedDelegateApiNames.Contains (apiName.Name))
|
|
throw new BindingException (1044, true, apiName.Name);
|
|
|
|
return apiName.Name;
|
|
}
|
|
|
|
string GetEventArgName (MethodInfo mi)
|
|
{
|
|
if (mi.GetParameters ().Length == 1)
|
|
return "EventArgs";
|
|
|
|
var a = AttributeManager.GetCustomAttribute<EventArgsAttribute> (mi);
|
|
if (a == null)
|
|
throw new BindingException (1004, true, mi.DeclaringType.FullName, mi.Name, mi.GetParameters ().Length);
|
|
|
|
var ea = (EventArgsAttribute) a;
|
|
if (ea.ArgName.EndsWith ("EventArgs", StringComparison.Ordinal))
|
|
throw new BindingException (1005, true, mi.DeclaringType.FullName, mi.Name);
|
|
|
|
if (ea.SkipGeneration){
|
|
skipGeneration [ea.FullName ? ea.ArgName : ea.ArgName + "EventArgs"] = true;
|
|
}
|
|
|
|
if (ea.FullName)
|
|
return ea.ArgName;
|
|
|
|
return ea.ArgName + "EventArgs";
|
|
}
|
|
|
|
string GetDelegateName (MethodInfo mi)
|
|
{
|
|
Attribute a = AttributeManager.GetCustomAttribute<DelegateNameAttribute> (mi);
|
|
if (a != null)
|
|
return ((DelegateNameAttribute) a).Name;
|
|
|
|
a = AttributeManager.GetCustomAttribute<EventArgsAttribute> (mi);
|
|
if (a == null)
|
|
throw new BindingException (1006, true, mi.DeclaringType.FullName, mi.Name);
|
|
|
|
ErrorHelper.Warning (1102, mi.DeclaringType.FullName, mi.Name);
|
|
return ((EventArgsAttribute) a).ArgName;
|
|
}
|
|
|
|
object GetDefaultValue (MethodInfo mi)
|
|
{
|
|
Attribute a = AttributeManager.GetCustomAttribute<DefaultValueAttribute> (mi);
|
|
if (a == null){
|
|
a = AttributeManager.GetCustomAttribute<DefaultValueFromArgumentAttribute> (mi);
|
|
if (a != null){
|
|
var fvfa = (DefaultValueFromArgumentAttribute) a;
|
|
return fvfa.Argument;
|
|
}
|
|
|
|
throw new BindingException (1016, true, mi.DeclaringType.FullName, mi.Name);
|
|
}
|
|
var def = ((DefaultValueAttribute) a).Default;
|
|
if (def == null)
|
|
return "null";
|
|
|
|
var type = def as Type;
|
|
if (type != null && (
|
|
type.FullName == "System.Drawing.PointF" ||
|
|
type.FullName == "System.Drawing.SizeF" ||
|
|
type.FullName == "System.Drawing.RectangleF" ||
|
|
type.FullName == "CoreGraphics.CGPoint" ||
|
|
type.FullName == "CoreGraphics.CGSize" ||
|
|
type.FullName == "CoreGraphics.CGRect"))
|
|
return type.FullName + ".Empty";
|
|
|
|
if (def is bool)
|
|
return (bool) def ? "true" : "false";
|
|
|
|
if (mi.ReturnType.IsEnum) {
|
|
if (def is string)
|
|
return def;
|
|
var name = Enum.GetName (mi.ReturnType, def);
|
|
if (string.IsNullOrEmpty (name)) {
|
|
// The value could be negative so it need to be enclosed in parenthesis
|
|
return "(" + mi.ReturnType.FullName + ") (" + def + ")";
|
|
} else {
|
|
return mi.ReturnType.FullName + "." + name;
|
|
}
|
|
}
|
|
|
|
return def;
|
|
}
|
|
|
|
bool HasNativeAttribute (ICustomAttributeProvider provider)
|
|
{
|
|
if (provider is null)
|
|
return false;
|
|
|
|
return AttributeManager.HasAttribute (provider, "NativeIntegerAttribute");
|
|
}
|
|
|
|
string RenderType (Type t, ICustomAttributeProvider provider = null)
|
|
{
|
|
t = GetCorrectGenericType (t);
|
|
|
|
if (!t.IsEnum){
|
|
switch (Type.GetTypeCode (t)){
|
|
case TypeCode.Char:
|
|
return "char";
|
|
case TypeCode.String:
|
|
return "string";
|
|
case TypeCode.Int32:
|
|
return "int";
|
|
case TypeCode.UInt32:
|
|
return "uint";
|
|
case TypeCode.Int64:
|
|
return "long";
|
|
case TypeCode.UInt64:
|
|
return "ulong";
|
|
case TypeCode.Single:
|
|
return "float";
|
|
case TypeCode.Double:
|
|
return "double";
|
|
case TypeCode.Decimal:
|
|
return "decimal";
|
|
case TypeCode.SByte:
|
|
return "sbyte";
|
|
case TypeCode.Byte:
|
|
return "byte";
|
|
case TypeCode.Boolean:
|
|
return "bool";
|
|
}
|
|
}
|
|
|
|
if (t == TypeManager.System_Void)
|
|
return "void";
|
|
|
|
if (t == TypeManager.System_IntPtr) {
|
|
return HasNativeAttribute (provider) ? "nint" : "IntPtr";
|
|
} else if (t == TypeManager.System_UIntPtr) {
|
|
return HasNativeAttribute (provider) ? "nuint" : "UIntPtr";
|
|
}
|
|
|
|
string ns = t.Namespace;
|
|
if (NamespaceManager.ImplicitNamespaces.Contains (ns) || t.IsGenericType) {
|
|
var targs = t.GetGenericArguments ();
|
|
if (targs.Length == 0)
|
|
return t.Name;
|
|
return $"global::{t.Namespace}." + RemoveArity (t.Name) + "<" + string.Join (", ", targs.Select (l => FormatTypeUsedIn (null, l)).ToArray ()) + ">";
|
|
}
|
|
if (NamespaceManager.NamespacesThatConflictWithTypes.Contains (ns))
|
|
return "global::" + t.FullName;
|
|
if (t.Name == t.Namespace)
|
|
return "global::" + t.FullName;
|
|
else
|
|
return t.FullName;
|
|
|
|
}
|
|
|
|
public static string Quote (string s)
|
|
{
|
|
if (s == null)
|
|
return String.Empty;
|
|
if (s == string.Empty)
|
|
return @"""""";
|
|
|
|
return $"@\"{s.Replace ("\"", "\"\"")}\"";
|
|
}
|
|
|
|
private static string FormatPropertyInfo (PropertyInfo pi)
|
|
{
|
|
return pi.DeclaringType.FullName + " " + pi.Name;
|
|
}
|
|
|
|
// Format a provider for error messages
|
|
public static string FormatProvider (ICustomAttributeProvider provider)
|
|
{
|
|
if (provider is Type type) {
|
|
return type.FullName;
|
|
} else if (provider is MemberInfo mi) {
|
|
return mi.DeclaringType.FullName + "." + mi.Name;
|
|
} else {
|
|
return provider?.ToString ();
|
|
}
|
|
}
|
|
}
|