Thanks to visit codestin.com
Credit goes to github.com

Skip to content
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
41 changes: 41 additions & 0 deletions src/PublicApiGenerator/DynamicContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System.CodeDom;

namespace PublicApiGenerator;

internal sealed class DynamicContext
{
private readonly bool[]? _transformFlags;
private int _index;

public DynamicContext(CodeAttributeDeclarationCollection attributes)
{
for (int i = 0; i < attributes.Count; ++i)
{
var attribute = attributes[i];
// return: System.Runtime.CompilerServices.DynamicAttribute is a workaround for CecilEx.MakeReturn.
if (attribute.Name == "System.Runtime.CompilerServices.DynamicAttribute" || attribute.Name == "return: System.Runtime.CompilerServices.DynamicAttribute")
{
if (attribute.Arguments.Count > 0)
{
// https://learn.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.dynamicattribute.-ctor
var expression = (CodeArrayCreateExpression)attribute.Arguments[0].Value;
_transformFlags = new bool[expression.Initializers.Count];
for (int j = 0; j < _transformFlags.Length; ++j)
{
_transformFlags[j] = (bool)((CodePrimitiveExpression)expression.Initializers[j]).Value;
}
}
else
{
_transformFlags = [];
}

break;
}
}
}

public void Move() => ++_index;

public bool IsDynamic => _transformFlags != null && (_transformFlags.Length == 0 || _transformFlags[_index]);
}
49 changes: 32 additions & 17 deletions src/PublicApiGenerator/System.CodeDom/CSharpCodeGenerator.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Licensed to the .NET Foundation under one or more agreements.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#pragma warning disable
Expand Down Expand Up @@ -216,7 +216,7 @@

private void OutputIdentifier(string ident) => Output.Write(CreateEscapedIdentifier(ident));

private void OutputType(CodeTypeReference typeRef) => Output.Write(GetTypeOutput(typeRef));
private void OutputType(CodeTypeReference typeRef, DynamicContext dynamicContext = null) => Output.Write(GetTypeOutput(typeRef, dynamicContext));

private void GenerateArrayCreateExpression(CodeArrayCreateExpression e)
{
Expand Down Expand Up @@ -1305,7 +1305,7 @@
}

OutputDirection(e.Direction);
OutputTypeNamePair(e.Type, e.Name);
OutputTypeNamePair(e.Type, e.Name, new DynamicContext(e.CustomAttributes));
}

private void GenerateEntryPointMethod(CodeEntryPointMethod e)
Expand Down Expand Up @@ -1390,7 +1390,7 @@
// interfaces still need "new"
OutputVTableModifier(e.Attributes);
}
OutputType(e.ReturnType);
OutputType(e.ReturnType, new DynamicContext(e.ReturnTypeCustomAttributes));
Output.Write(' ');
if (e.PrivateImplementationType != null)
{
Expand Down Expand Up @@ -1989,7 +1989,7 @@

CodeTypeDelegate del = (CodeTypeDelegate)e;
Output.Write("delegate ");
OutputType(del.ReturnType);
OutputType(del.ReturnType, new DynamicContext(del.CustomAttributes));
Output.Write(' ');
OutputIdentifier(e.Name);
Output.Write('(');
Expand All @@ -2004,6 +2004,7 @@
OutputTypeParameters(e.TypeParameters);

bool first = true;
var dynamicContext = new DynamicContext(e.CustomAttributes);
foreach (CodeTypeReference typeRef in e.BaseTypes)
{
if (first)
Expand All @@ -2015,7 +2016,7 @@
{
Output.Write(", ");
}
OutputType(typeRef);
OutputType(typeRef, dynamicContext);
}

OutputTypeParameterConstraints(e.TypeParameters);
Expand Down Expand Up @@ -2309,9 +2310,9 @@
}
}

private void OutputTypeNamePair(CodeTypeReference typeRef, string name)
private void OutputTypeNamePair(CodeTypeReference typeRef, string name, DynamicContext dynamicContext = null)
{
OutputType(typeRef);
OutputType(typeRef, dynamicContext);
Output.Write(' ');
OutputIdentifier(name);
}
Expand Down Expand Up @@ -2687,6 +2688,13 @@
continue;
}

// Do not print DynamicAttribute, use dynamic keyword instead of object.
// return: System.Runtime.CompilerServices.DynamicAttribute is a workaround for CecilEx.MakeReturn.
if (current.Name.Equals("System.Runtime.CompilerServices.DynamicAttribute") || current.Name.Equals("return: System.Runtime.CompilerServices.DynamicAttribute"))
{
continue;
}

GenerateAttributeDeclarationsStart();
if (prefix != null)
{
Expand Down Expand Up @@ -2825,7 +2833,7 @@
}

// returns the type name without any array declaration.
private string GetBaseTypeOutput(CodeTypeReference typeRef, bool preferBuiltInTypes = true)
private string GetBaseTypeOutput(CodeTypeReference typeRef, bool preferBuiltInTypes = true, DynamicContext dynamicContext = null)
{
string s = typeRef.BaseType;

Expand All @@ -2849,7 +2857,9 @@
case "system.string":
return "string";
case "system.object":
return "object";
return dynamicContext?.IsDynamic == true ? "dynamic" : "object";
case "object?":
return dynamicContext?.IsDynamic == true ? "dynamic?" : "object?";
case "system.boolean":
return "bool";
case "system.void":
Expand Down Expand Up @@ -2909,7 +2919,7 @@
i++;
}

GetTypeArgumentsOutput(typeRef.TypeArguments, currentTypeArgStart, numTypeArgs, sb);
GetTypeArgumentsOutput(typeRef.TypeArguments, currentTypeArgStart, numTypeArgs, sb, dynamicContext);
currentTypeArgStart += numTypeArgs;

// Arity can be in the middle of a nested type name, so we might have a . or + after it.
Expand All @@ -2934,11 +2944,11 @@
private string GetTypeArgumentsOutput(CodeTypeReferenceCollection typeArguments)
{
var sb = new StringBuilder(128);
GetTypeArgumentsOutput(typeArguments, 0, typeArguments.Count, sb);
GetTypeArgumentsOutput(typeArguments, 0, typeArguments.Count, sb, null);

Check warning on line 2947 in src/PublicApiGenerator/System.CodeDom/CSharpCodeGenerator.cs

View check run for this annotation

Codecov / codecov/patch

src/PublicApiGenerator/System.CodeDom/CSharpCodeGenerator.cs#L2947

Added line #L2947 was not covered by tests
return sb.ToString();
}

private void GetTypeArgumentsOutput(CodeTypeReferenceCollection typeArguments, int start, int length, StringBuilder sb)
private void GetTypeArgumentsOutput(CodeTypeReferenceCollection typeArguments, int start, int length, StringBuilder sb, DynamicContext dynamicContext)
{
sb.Append('<');
bool first = true;
Expand All @@ -2956,12 +2966,17 @@
// it's possible that we call GetTypeArgumentsOutput with an empty typeArguments collection. This is the case
// for open types, so we want to just output the brackets and commas.
if (i < typeArguments.Count)
sb.Append(GetTypeOutput(typeArguments[i]));
{
dynamicContext?.Move();
sb.Append(GetTypeOutput(typeArguments[i], dynamicContext));
}
}
sb.Append('>');
}

public string GetTypeOutput(CodeTypeReference typeRef)
public string GetTypeOutput(CodeTypeReference typeRef) => GetTypeOutput(typeRef, null);

public string GetTypeOutput(CodeTypeReference typeRef, DynamicContext dynamicContext)
{
string s = string.Empty;

Expand All @@ -2970,7 +2985,7 @@
{
baseTypeRef = baseTypeRef.ArrayElementType;
}
s += GetBaseTypeOutput(baseTypeRef);
s += GetBaseTypeOutput(baseTypeRef, dynamicContext: dynamicContext);

while (typeRef != null && typeRef.ArrayRank > 0)
{
Expand Down Expand Up @@ -3182,4 +3197,4 @@
}
}
}
}
}
11 changes: 11 additions & 0 deletions src/PublicApiGeneratorTests/Delegate_types.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ namespace PublicApiGeneratorTests
{
public class Delegate_types : ApiGeneratorTestsBase
{
[Fact]
public void Should_output_dynamic_delegate()
{
AssertPublicApi<DynamicDelegate>(
@"namespace PublicApiGeneratorTests.Examples
{
public delegate dynamic DynamicDelegate(dynamic v);
}");
}

[Fact]
public void Should_output_void_delegate()
{
Expand Down Expand Up @@ -57,6 +67,7 @@ public void Should_output_generic_type_parameters()

namespace Examples
{
public delegate dynamic DynamicDelegate(dynamic v);
public delegate void VoidDelegate();
public delegate int DelegateWithPrimitiveParameters(int v1, string v2);
public delegate ComplexType DelegateWithComplexParameters(ComplexType v1);
Expand Down
25 changes: 20 additions & 5 deletions src/PublicApiGeneratorTests/Dynamics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,33 @@ namespace PublicApiGeneratorTests
{
public class Dynamics : ApiGeneratorTestsBase
{
[Fact(Skip = "Needs investigation")]
[Fact]
public void Should_output_dynamic()
{
AssertPublicApi<ClassWithDynamic>(
@"namespace PublicApiGeneratorTests.Examples
{
public class ClassWithDynamic : System.Collections.Generic.List<dynamic>
{
public class ClassWithDynamic2 : System.Collections.Generic.List<dynamic?> { }
public ClassWithDynamic() { }
public dynamic DoIt1(dynamic p) { }
public dynamic? DoIt2(dynamic? p) { }
public System.Collections.Generic.Dictionary<dynamic, object> DoIt3(System.Collections.Generic.Dictionary<dynamic, object>? p) { }
public class ClassWithDynamic2 : System.Collections.Generic.List<dynamic?>
{
public ClassWithDynamic2() { }
}
public class ClassWithDynamic3 : System.Collections.Generic.Dictionary<dynamic, object>
{
public ClassWithDynamic3() { }
}
public class ClassWithDynamic4 : System.Collections.Generic.Dictionary<object, dynamic>
{
public ClassWithDynamic4() { }
}
}
}");
}

// TODO: Enum with flags + undefined value
// Not supported by Cecil?
}

namespace Examples
Expand All @@ -30,9 +39,15 @@ public class ClassWithDynamic : List<dynamic>
{
public class ClassWithDynamic2 : List<dynamic?> { }

public class ClassWithDynamic3 : Dictionary<dynamic, object> { }

public class ClassWithDynamic4 : Dictionary<object, dynamic> { }

public dynamic DoIt1(dynamic p) { throw null; }

public dynamic? DoIt2(dynamic? p) { throw null; }

public Dictionary<dynamic, object> DoIt3(Dictionary<dynamic, object>? p) { throw null; }
}
}
}
Loading