Skip to content

Commit

Permalink
[RGen] Add extension method to know if a symbol is wrapped.
Browse files Browse the repository at this point in the history
  • Loading branch information
mandel-macaque committed Jan 30, 2025
1 parent 8d57fe6 commit afdd025
Show file tree
Hide file tree
Showing 4 changed files with 210 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,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 (
Expand All @@ -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;
Expand All @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Up @@ -1702,5 +1702,162 @@ void IsBlittable (ApplePlatform platform, string inputText, bool expectedResult)
Assert.NotNull (symbol);
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 ());
}

}

0 comments on commit afdd025

Please sign in to comment.