Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RGen] Add extension method to know if a symbol is wrapped. #22077

Merged
merged 2 commits into from
Jan 30, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -8,6 +8,16 @@ namespace Microsoft.Macios.Generator.DataModel;

readonly partial struct TypeInfo {

/// <summary>
/// Return if the type represents a wrapped object from the objc world.
/// </summary>
public bool IsWrapped { get; init; }

/// <summary>
/// Returns, if the type is an array, if its elements are a wrapped object from the objc world.
/// </summary>
public bool ArrayElementTypeIsWrapped { get; init; }

internal TypeInfo (ITypeSymbol symbol) :
this (
symbol is IArrayTypeSymbol arrayTypeSymbol
@@ -18,7 +28,6 @@ symbol is IArrayTypeSymbol arrayTypeSymbol
IsNullable = symbol.NullableAnnotation == NullableAnnotation.Annotated;
IsBlittable = symbol.IsBlittable ();
IsSmartEnum = symbol.IsSmartEnum ();
IsArray = symbol is IArrayTypeSymbol;
IsReferenceType = symbol.IsReferenceType;
IsStruct = symbol.TypeKind == TypeKind.Struct;
IsInterface = symbol.TypeKind == TypeKind.Interface;
@@ -33,6 +42,13 @@ symbol is IArrayTypeSymbol arrayTypeSymbol
parents: out parents,
interfaces: out interfaces);

IsWrapped = symbol.IsWrapped (isNSObject);
if (symbol is IArrayTypeSymbol arraySymbol) {
IsArray = true;
ArrayElementTypeIsWrapped = arraySymbol.ElementType.IsWrapped ();
}
IsArray = symbol is IArrayTypeSymbol;

// try to get the named type symbol to have more educated decisions
var namedTypeSymbol = symbol as INamedTypeSymbol;

Original file line number Diff line number Diff line change
@@ -13,6 +13,11 @@
namespace Microsoft.Macios.Generator.Extensions;

static partial class TypeSymbolExtensions {

const string nativeObjectInterface = "ObjCRuntime.INativeObject";
const string nsObjectClass = "Foundation.NSObject";
const string dictionaryContainerClass = "Foundation.DictionaryContainer";

/// <summary>
/// Retrieve a dictionary with the attribute data of all the attributes attached to a symbol. Because
/// an attribute can appear more than once, the valus are a collection of attribute data.
@@ -417,11 +422,7 @@ public static void GetInheritance (
out ImmutableArray<string> parents,
out ImmutableArray<string> interfaces)
{
const string nativeObjectInterface = "ObjCRuntime.INativeObject";
const string nsObjectClass = "Foundation.NSObject";
const string dictionaryContainerClass = "Foundation.DictionaryContainer";

isNSObject = false;
isNSObject = symbol.ToDisplayString ().Trim () == nsObjectClass;
isNativeObject = false;
isDictionaryContainer = false;

Original file line number Diff line number Diff line change
@@ -3,6 +3,8 @@

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.Macios.Generator.Attributes;
using Microsoft.Macios.Generator.Availability;
@@ -213,4 +215,32 @@ public static bool NeedsStret (this ITypeSymbol returnType, Compilation compilat

return ArmNeedStret (returnType, compilation);
}

/// <summary>
/// A type is considered wrapped if it is an Interface or is an child of the NSObject class or the NSObject
/// itself.
/// </summary>
/// <param name="symbol">The symbol to check if it is wrapped.</param>
/// <param name="isNSObject">If the symbol is a NSObject of inherits from an NSObject.</param>
/// <returns>True if the ymbol is considered to be wrapped.</returns>
public static bool IsWrapped (this ITypeSymbol symbol, bool isNSObject)
=> symbol.TypeKind == TypeKind.Interface || isNSObject;

/// <summary>
/// A type is considered wrapped if it is an Interface or is an child of the NSObject class or the NSObject
/// itself.
/// </summary>
/// <param name="symbol">The symbol to check if it is wrapped.</param>
/// <returns>True if the ymbol is considered to be wrapped.</returns>
public static bool IsWrapped (this ITypeSymbol symbol)
{
symbol.GetInheritance (
isNSObject: out bool isNSObject,
isNativeObject: out bool _,
isDictionaryContainer: out bool _,
parents: out ImmutableArray<string> _,
interfaces: out ImmutableArray<string> _);
// either we are a NSObject or we are a subclass of it
return IsWrapped (symbol, isNSObject);
}
}
Original file line number Diff line number Diff line change
@@ -1703,4 +1703,161 @@ void IsBlittable (ApplePlatform platform, string inputText, bool expectedResult)
Assert.Equal (expectedResult, symbol.Type.IsBlittable ());
}

class TestDataIsWrapped : IEnumerable<object []> {
public IEnumerator<object []> GetEnumerator ()
{
const string stringProperty = @"
using System;
using ObjCBindings;
namespace NS;
[BindingType<Class>]
public partial class MyClass {
public string Property { get; set; }
}
";
yield return [stringProperty, false];

const string nsUuidProperty = @"
using System;
using Foundation;
using ObjCBindings;
namespace NS;
[BindingType<Class>]
public partial class MyClass {
public NSUuid Property { get; set; }
}
";
yield return [nsUuidProperty, true];

const string nmatrix4Property = @"
using System;
using CoreGraphics;
using ObjCBindings;
namespace NS;
[BindingType<Class>]
public partial class MyClass {
public NMatrix4 Property { get; set; }
}
";

yield return [nmatrix4Property, false];

const string nativeHandleProperty = @"
using System;
using ObjCRuntime;
using ObjCBindings;
namespace NS;
[BindingType<Class>]
public partial class MyClass {
public NativeHandle Property { get; set; }
}
";
yield return [nativeHandleProperty, false];

const string nsZoneProperty = @"
using System;
using Foundation;
using ObjCBindings;
namespace NS;
[BindingType<Class>]
public partial class MyClass {
public NSZone Property { get; set; }
}
";
yield return [nsZoneProperty, false];

const string nsobjectProperty = @"
using System;
using Foundation;
using ObjCBindings;
namespace NS;
[BindingType<Class>]
public partial class MyClass {
public NSObject Property { get; set; }
}
";
yield return [nsobjectProperty, true];

const string nssetProperty = @"
using System;
using Foundation;
using ObjCBindings;
namespace NS;
[BindingType<Class>]
public partial class MyClass {
public NSSet<NSObject> Property { get; set; }
}
";
yield return [nssetProperty, true];


const string mtlDeviceProperty = @"
using System;
using Metal;
using ObjCBindings;
namespace NS;
[BindingType<Class>]
public partial class MyClass {
public IMTLDevice Property { get; set; }
}
";
yield return [mtlDeviceProperty, true];

const string EnumProperty = @"
using System;
using System.Runtime.InteropServices;
using ObjCBindings;
namespace NS;
public enum MyEnum : ulong {
First,
Second,
}
[BindingType<Class>]
public partial class MyClass {
public MyEnum Property { get; set; }
}
";
yield return [EnumProperty, false];
}

IEnumerator IEnumerable.GetEnumerator () => GetEnumerator ();
}

[Theory]
[AllSupportedPlatformsClassData<TestDataIsWrapped>]
void IsWrapped (ApplePlatform platform, string inputText, bool expectedResult)
{
var (compilation, syntaxTrees) = CreateCompilation (platform, sources: inputText);
Assert.Single (syntaxTrees);
var declaration = syntaxTrees [0].GetRoot ()
.DescendantNodes ()
.OfType<PropertyDeclarationSyntax> ()
.FirstOrDefault ();
Assert.NotNull (declaration);
var semanticModel = compilation.GetSemanticModel (syntaxTrees [0]);
Assert.NotNull (semanticModel);
var symbol = semanticModel.GetDeclaredSymbol (declaration);
Assert.NotNull (symbol);
Assert.Equal (expectedResult, symbol.Type.IsWrapped ());
}

}