Skip to content

Commit 540182f

Browse files
[Rgen] Add code needed to parse the several snippet attrs.
Allow the transformer to detect this. We will need to think a way to port them to the new API style.
1 parent 8d57fe6 commit 540182f

File tree

3 files changed

+186
-0
lines changed

3 files changed

+186
-0
lines changed
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
using System.Diagnostics.CodeAnalysis;
5+
using Microsoft.CodeAnalysis;
6+
7+
namespace Microsoft.Macios.Transformer.Attributes;
8+
9+
readonly struct SnippetData : IEquatable<SnippetData> {
10+
11+
public string Code { get; }
12+
13+
public bool Optimizable { get; }
14+
15+
public SnippetData (string code)
16+
{
17+
Code = code;
18+
}
19+
20+
public SnippetData (string code, bool optimizable)
21+
{
22+
Code = code;
23+
Optimizable = optimizable;
24+
}
25+
26+
public static bool TryParse (AttributeData attributeData,
27+
[NotNullWhen (true)] out SnippetData? data)
28+
{
29+
data = null;
30+
var count = attributeData.ConstructorArguments.Length;
31+
string code;
32+
var optimizable = false;
33+
34+
// custom marshal directive values
35+
36+
switch (count) {
37+
case 1:
38+
code = (string) attributeData.ConstructorArguments [0].Value!;
39+
break;
40+
default:
41+
// 0 should not be an option..
42+
return false;
43+
}
44+
45+
if (attributeData.NamedArguments.Length == 0) {
46+
data = new (code);
47+
return true;
48+
}
49+
50+
foreach (var (argumentName, value) in attributeData.NamedArguments) {
51+
switch (argumentName) {
52+
case "Code":
53+
code = (string?) value.Value!;
54+
break;
55+
case "Optimizable":
56+
optimizable = (bool) value.Value!;
57+
break;
58+
default:
59+
data = null;
60+
return false;
61+
}
62+
}
63+
64+
data = new (code, optimizable);
65+
return true;
66+
}
67+
68+
public bool Equals (SnippetData other)
69+
{
70+
if (Code != other.Code)
71+
return false;
72+
return Optimizable == other.Optimizable;
73+
}
74+
75+
/// <inheritdoc />
76+
public override bool Equals (object? obj)
77+
{
78+
return obj is SnippetData other && Equals (other);
79+
}
80+
81+
/// <inheritdoc />
82+
public override int GetHashCode ()
83+
=> HashCode.Combine (Code, Optimizable);
84+
85+
public static bool operator == (SnippetData x, SnippetData y)
86+
{
87+
return x.Equals (y);
88+
}
89+
90+
public static bool operator != (SnippetData x, SnippetData y)
91+
{
92+
return !(x == y);
93+
}
94+
95+
public override string ToString ()
96+
=> $"{{ Code: '{Code}' Optimizable: {Optimizable} }}";
97+
}

src/rgen/Microsoft.Macios.Transformer/AttributesNames.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,13 @@ static class AttributesNames {
8383
[BindingFlag (AttributeTargets.Parameter | AttributeTargets.Property)]
8484
public const string DisableZeroCopyAttribute = "DisableZeroCopyAttribute";
8585

86+
/// <summary>
87+
/// Code to run from a generated Dispose method, before any generated code is executed
88+
/// Adding this attribute will, by default, make the method non-optimizable by the SDK tools
89+
/// </summary>
90+
[BindingAttribute(typeof(ErrorDomainData), AttributeTargets.Interface | AttributeTargets.Class)]
8691
public const string DisposeAttribute = "DisposeAttribute";
92+
8793
public const string EditorBrowsableAttribute = "System.ComponentModel.EditorBrowsableAttribute";
8894

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

229+
/// <summary>
230+
/// PostSnippet code is inserted before returning, before paramters are disposed/released
231+
/// Adding this attribute will, by default, make the method non-optimizable by the SDK tools
232+
/// </summary>
233+
[BindingAttribute (typeof(SnippetData), AttributeTargets.Method | AttributeTargets.Property)]
234+
public const string PostSnippetAttribute = "PostSnippetAttribute";
235+
236+
/// <summary>
237+
/// PreSnippet code is inserted after the parameters have been validated/marshalled
238+
/// Adding this attribute will, by default, make the method non-optimizable by the SDK tools
239+
/// </summary>
240+
[BindingAttribute (typeof(SnippetData), AttributeTargets.Method | AttributeTargets.Property)]
241+
public const string PreSnippetAttribute = "PreSnippetAttribute";
242+
223243
/// <summary>
224244
/// When this attribute is applied to the interface definition it will flag the default constructor as private.
225245
/// </summary>
@@ -228,6 +248,13 @@ static class AttributesNames {
228248

229249
[BindingFlag (AttributeTargets.Property)]
230250
public const string ProbePresenceAttribute = "ProbePresenceAttribute";
251+
252+
/// <summary>
253+
/// PrologueSnippet code is inserted before any code is generated
254+
/// Adding this attribute will, by default, make the method non-optimizable by the SDK tools
255+
/// </summary>
256+
[BindingAttribute (typeof(SnippetData), AttributeTargets.Method | AttributeTargets.Property)]
257+
public const string PrologueSnippetAttribute = "PrologueSnippetAttribute";
231258

232259
/// <summary>
233260
/// Use this attribute to instruct the binding generator that the binding for this particular method should be
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
using System.Collections;
5+
using Microsoft.CodeAnalysis.CSharp;
6+
using Microsoft.CodeAnalysis.CSharp.Syntax;
7+
using Microsoft.Macios.Generator.Extensions;
8+
using Microsoft.Macios.Transformer.Attributes;
9+
using Xamarin.Tests;
10+
using Xamarin.Utils;
11+
12+
namespace Microsoft.Macios.Transformer.Tests.Attributes;
13+
14+
public class SnippetDataTests : BaseTransformerTestClass {
15+
class TestDataTryCreate : IEnumerable<object []> {
16+
17+
public IEnumerator<object []> GetEnumerator ()
18+
{
19+
const string path = "/some/random/path.cs";
20+
21+
const string disposeAttribute = @"
22+
using System;
23+
using Foundation;
24+
using ObjCRuntime;
25+
using UIKit;
26+
27+
namespace Test;
28+
29+
[BaseType (typeof (NSControl))]
30+
[Dispose (""dispatcher = null;"", Optimizable = true)]
31+
interface NSButton { }
32+
";
33+
34+
yield return [(Source: disposeAttribute, Path: path), new SnippetData ("dispatcher = null;", true)];
35+
}
36+
37+
IEnumerator IEnumerable.GetEnumerator () => GetEnumerator ();
38+
}
39+
40+
[Theory]
41+
[AllSupportedPlatformsClassData<TestDataTryCreate>]
42+
void TryCreateTests (ApplePlatform platform, (string Source, string Path) source, SnippetData expectedData)
43+
{
44+
// create a compilation used to create the transformer
45+
var compilation = CreateCompilation (platform, sources: source);
46+
var syntaxTree = compilation.SyntaxTrees.ForSource (source);
47+
Assert.NotNull (syntaxTree);
48+
49+
var semanticModel = compilation.GetSemanticModel (syntaxTree);
50+
Assert.NotNull (semanticModel);
51+
52+
var declaration = syntaxTree.GetRoot ()
53+
.DescendantNodes ().OfType<BaseTypeDeclarationSyntax> ()
54+
.FirstOrDefault ();
55+
Assert.NotNull (declaration);
56+
57+
var symbol = semanticModel.GetDeclaredSymbol (declaration);
58+
Assert.NotNull (symbol);
59+
var attribute = symbol.GetAttribute<SnippetData> (AttributesNames.DisposeAttribute, SnippetData.TryParse);
60+
Assert.Equal (expectedData, attribute);
61+
}
62+
}

0 commit comments

Comments
 (0)