Skip to content

Commit

Permalink
[Rgen] Add code needed to parse the several snippet attrs.
Browse files Browse the repository at this point in the history
Allow the transformer to detect this. We will need to think a way to
port them to the new API style.
  • Loading branch information
mandel-macaque committed Jan 27, 2025
1 parent d79592d commit 694a975
Show file tree
Hide file tree
Showing 3 changed files with 186 additions and 0 deletions.
97 changes: 97 additions & 0 deletions src/rgen/Microsoft.Macios.Transformer/Attributes/SnippetData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis;

namespace Microsoft.Macios.Transformer.Attributes;

readonly struct SnippetData : IEquatable<SnippetData> {

public string Code { get; }

public bool Optimizable { get; }

public SnippetData (string code)
{
Code = code;
}

public SnippetData (string code, bool optimizable)
{
Code = code;
Optimizable = optimizable;
}

public static bool TryParse (AttributeData attributeData,
[NotNullWhen (true)] out SnippetData? data)
{
data = null;
var count = attributeData.ConstructorArguments.Length;
string code;
var optimizable = false;

// custom marshal directive values

switch (count) {
case 1:
code = (string) attributeData.ConstructorArguments [0].Value!;
break;
default:
// 0 should not be an option..
return false;
}

if (attributeData.NamedArguments.Length == 0) {
data = new (code);
return true;
}

foreach (var (argumentName, value) in attributeData.NamedArguments) {
switch (argumentName) {
case "Code":
code = (string?) value.Value!;
break;
case "Optimizable":
optimizable = (bool) value.Value!;
break;
default:
data = null;
return false;
}
}

data = new (code, optimizable);
return true;
}

public bool Equals (SnippetData other)
{
if (Code != other.Code)
return false;
return Optimizable == other.Optimizable;
}

/// <inheritdoc />
public override bool Equals (object? obj)
{
return obj is SnippetData other && Equals (other);
}

/// <inheritdoc />
public override int GetHashCode ()
=> HashCode.Combine (Code, Optimizable);

public static bool operator == (SnippetData x, SnippetData y)
{
return x.Equals (y);
}

public static bool operator != (SnippetData x, SnippetData y)
{
return !(x == y);
}

public override string ToString ()
=> $"{{ Code: '{Code}' Optimizable: {Optimizable} }}";
}
27 changes: 27 additions & 0 deletions src/rgen/Microsoft.Macios.Transformer/AttributesNames.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,13 @@ static class AttributesNames {
[BindingFlag (AttributeTargets.Parameter | AttributeTargets.Property)]
public const string DisableZeroCopyAttribute = "DisableZeroCopyAttribute";

/// <summary>
/// Code to run from a generated Dispose method, before any generated code is executed
/// Adding this attribute will, by default, make the method non-optimizable by the SDK tools
/// </summary>
[BindingAttribute(typeof(ErrorDomainData), AttributeTargets.Interface | AttributeTargets.Class)]
public const string DisposeAttribute = "DisposeAttribute";

public const string EditorBrowsableAttribute = "System.ComponentModel.EditorBrowsableAttribute";

[BindingAttribute(typeof(ErrorDomainData), AttributeTargets.Enum)]
Expand Down Expand Up @@ -220,6 +226,20 @@ static class AttributesNames {
[BindingFlag]
public const string PlainStringAttribute = "PlainStringAttribute";

/// <summary>
/// PostSnippet code is inserted before returning, before paramters are disposed/released
/// Adding this attribute will, by default, make the method non-optimizable by the SDK tools
/// </summary>
[BindingAttribute (typeof(SnippetData), AttributeTargets.Method | AttributeTargets.Property)]
public const string PostSnippetAttribute = "PostSnippetAttribute";

/// <summary>
/// PreSnippet code is inserted after the parameters have been validated/marshalled
/// Adding this attribute will, by default, make the method non-optimizable by the SDK tools
/// </summary>
[BindingAttribute (typeof(SnippetData), AttributeTargets.Method | AttributeTargets.Property)]
public const string PreSnippetAttribute = "PreSnippetAttribute";

/// <summary>
/// When this attribute is applied to the interface definition it will flag the default constructor as private.
/// </summary>
Expand All @@ -228,6 +248,13 @@ static class AttributesNames {

[BindingFlag (AttributeTargets.Property)]
public const string ProbePresenceAttribute = "ProbePresenceAttribute";

/// <summary>
/// PrologueSnippet code is inserted before any code is generated
/// Adding this attribute will, by default, make the method non-optimizable by the SDK tools
/// </summary>
[BindingAttribute (typeof(SnippetData), AttributeTargets.Method | AttributeTargets.Property)]
public const string PrologueSnippetAttribute = "PrologueSnippetAttribute";

/// <summary>
/// Use this attribute to instruct the binding generator that the binding for this particular method should be
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Collections;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.Macios.Generator.Extensions;
using Microsoft.Macios.Transformer.Attributes;
using Xamarin.Tests;
using Xamarin.Utils;

namespace Microsoft.Macios.Transformer.Tests.Attributes;

public class SnippetDataTests : BaseTransformerTestClass {
class TestDataTryCreate : IEnumerable<object []> {

public IEnumerator<object []> GetEnumerator ()
{
const string path = "/some/random/path.cs";

const string disposeAttribute = @"
using System;
using Foundation;
using ObjCRuntime;
using UIKit;
namespace Test;
[BaseType (typeof (NSControl))]
[Dispose (""dispatcher = null;"", Optimizable = true)]
interface NSButton { }
";

yield return [(Source: disposeAttribute, Path: path), new SnippetData("dispatcher = null;", true)];
}

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

[Theory]
[AllSupportedPlatformsClassData<TestDataTryCreate>]
void TryCreateTests (ApplePlatform platform, (string Source, string Path) source, SnippetData expectedData)
{
// create a compilation used to create the transformer
var compilation = CreateCompilation (platform, sources: source);
var syntaxTree = compilation.SyntaxTrees.ForSource (source);
Assert.NotNull (syntaxTree);

var semanticModel = compilation.GetSemanticModel (syntaxTree);
Assert.NotNull (semanticModel);

var declaration = syntaxTree.GetRoot ()
.DescendantNodes ().OfType<BaseTypeDeclarationSyntax> ()
.FirstOrDefault ();
Assert.NotNull (declaration);

var symbol = semanticModel.GetDeclaredSymbol (declaration);
Assert.NotNull (symbol);
var attribute = symbol.GetAttribute<SnippetData> (AttributesNames.DisposeAttribute, SnippetData.TryParse);
Assert.Equal (expectedData, attribute);
}
}

0 comments on commit 694a975

Please sign in to comment.