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 code needed to parse the several snippet attrs. #22058

Merged
merged 1 commit into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
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
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);
}
}
Loading