Skip to content

Commit

Permalink
Mark forwarders for all TypeReferences (dotnet#2276)
Browse files Browse the repository at this point in the history
* Build testcases against reference assemblies

* Reproduce issue in testcase

* Add TypeReferenceMarker

* Update one more testcase

* Don't mark forwarders in TypeReferenceMarker

Leave the logic as before and mark them from MarkStep.

* PR feedback

- Use ref assemblies for ReferenceAttribute
- Avoid adding to common references unless needed by test dependencies
  • Loading branch information
sbomer committed Sep 17, 2021
1 parent 8977a27 commit 9ada810
Show file tree
Hide file tree
Showing 22 changed files with 493 additions and 76 deletions.
5 changes: 5 additions & 0 deletions src/linker/Linker.Steps/MarkStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1385,6 +1385,11 @@ void MarkEntireAssembly (AssemblyDefinition assembly)
MarkingHelpers.MarkForwardedScope (new TypeReference (exportedType.Namespace, exportedType.Name, module, exportedType.Scope));
}

// Mark scopes of type references by traversing the assembly.
new TypeReferenceMarker (assembly, MarkingHelpers).Process ();

// Also mark the scopes of metadata typeref rows to cover any not discovered by the traversal.
// This can happen when the compiler emits typerefs into IL which aren't strictly necessary per ECMA 335.
foreach (TypeReference typeReference in module.GetTypeReferences ())
MarkingHelpers.MarkForwardedScope (typeReference);
}
Expand Down
343 changes: 343 additions & 0 deletions src/linker/Linker/TypeReferenceMarker.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,343 @@
// Licensed to the.NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Collections.Generic;

namespace Mono.Linker
{
struct TypeReferenceMarker
{
readonly AssemblyDefinition assembly;
readonly MarkingHelpers markingHelpers;
HashSet<TypeReference> visited;

public TypeReferenceMarker (AssemblyDefinition assembly, MarkingHelpers markingHelpers)
{
this.assembly = assembly;
this.markingHelpers = markingHelpers;
visited = null;
}

// Traverse the assembly and mark the scopes of discovered type references (but not exported types).
// This includes scopes referenced by Cecil TypeReference objects that don't represent rows in the typeref table,
// such as references to built-in types, or attribute arguments which encode type references as strings.
public void Process ()
{
visited = new HashSet<TypeReference> ();

WalkCustomAttributesTypesScopes (assembly);
WalkSecurityAttributesTypesScopes (assembly);

foreach (var module in assembly.Modules)
WalkCustomAttributesTypesScopes (module);

var mmodule = assembly.MainModule;
if (mmodule.HasTypes) {
foreach (var type in mmodule.Types) {
WalkScopes (type);
}
}

visited = null;
}

void WalkScopes (TypeDefinition typeDefinition)
{
WalkCustomAttributesTypesScopes (typeDefinition);
WalkSecurityAttributesTypesScopes (typeDefinition);

if (typeDefinition.BaseType != null)
WalkScopeOfTypeReference (typeDefinition.BaseType);

if (typeDefinition.HasInterfaces) {
foreach (var iface in typeDefinition.Interfaces) {
WalkCustomAttributesTypesScopes (iface);
WalkScopeOfTypeReference (iface.InterfaceType);
}
}

if (typeDefinition.HasGenericParameters)
WalkTypeScope (typeDefinition.GenericParameters);

if (typeDefinition.HasEvents) {
foreach (var e in typeDefinition.Events) {
WalkCustomAttributesTypesScopes (e);
// e.EventType is not saved
}
}

if (typeDefinition.HasFields) {
foreach (var f in typeDefinition.Fields) {
WalkCustomAttributesTypesScopes (f);
WalkScopeOfTypeReference (f.FieldType);
WalkMarshalInfoTypeScope (f);
}
}

if (typeDefinition.HasMethods) {
foreach (var m in typeDefinition.Methods) {
WalkCustomAttributesTypesScopes (m);
WalkSecurityAttributesTypesScopes (m);
if (m.HasGenericParameters)
WalkTypeScope (m.GenericParameters);

WalkCustomAttributesTypesScopes (m.MethodReturnType);
WalkScopeOfTypeReference (m.MethodReturnType.ReturnType);
WalkMarshalInfoTypeScope (m.MethodReturnType);
if (m.HasOverrides) {
foreach (var mo in m.Overrides)
WalkMethodReference (mo);
}

if (m.HasParameters)
WalkTypeScope (m.Parameters);

if (m.HasBody)
WalkTypeScope (m.Body);
}
}

if (typeDefinition.HasProperties) {
foreach (var p in typeDefinition.Properties) {
WalkCustomAttributesTypesScopes (p);
// p.PropertyType is not saved
}
}

if (typeDefinition.HasNestedTypes) {
foreach (var nestedType in typeDefinition.NestedTypes) {
WalkScopes (nestedType);
}
}
}

void WalkTypeScope (Collection<GenericParameter> genericParameters)
{
foreach (var gp in genericParameters) {
WalkCustomAttributesTypesScopes (gp);
if (gp.HasConstraints)
WalkTypeScope (gp.Constraints);
}
}

void WalkTypeScope (Collection<GenericParameterConstraint> constraints)
{
foreach (var gc in constraints) {
WalkCustomAttributesTypesScopes (gc);
WalkScopeOfTypeReference (gc.ConstraintType);
}
}

void WalkTypeScope (Collection<ParameterDefinition> parameters)
{
foreach (var p in parameters) {
WalkCustomAttributesTypesScopes (p);
WalkScopeOfTypeReference (p.ParameterType);
WalkMarshalInfoTypeScope (p);
}
}

void WalkTypeScope (MethodBody body)
{
if (body.HasVariables) {
foreach (var v in body.Variables) {
WalkScopeOfTypeReference (v.VariableType);
}
}

if (body.HasExceptionHandlers) {
foreach (var eh in body.ExceptionHandlers) {
if (eh.CatchType != null)
WalkScopeOfTypeReference (eh.CatchType);
}
}

foreach (var instr in body.Instructions) {
switch (instr.OpCode.OperandType) {

case OperandType.InlineMethod: {
var mr = (MethodReference) instr.Operand;
WalkMethodReference (mr);
break;
}

case OperandType.InlineField: {
var fr = (FieldReference) instr.Operand;
WalkFieldReference (fr);
break;
}

case OperandType.InlineTok: {
switch (instr.Operand) {
case TypeReference tr:
WalkScopeOfTypeReference (tr);
break;
case FieldReference fr:
WalkFieldReference (fr);
break;
case MethodReference mr:
WalkMethodReference (mr);
break;
}

break;
}

case OperandType.InlineType: {
var tr = (TypeReference) instr.Operand;
WalkScopeOfTypeReference (tr);
break;
}
}
}
}

void WalkMethodReference (MethodReference mr)
{
WalkScopeOfTypeReference (mr.ReturnType);
WalkScopeOfTypeReference (mr.DeclaringType);

if (mr is GenericInstanceMethod gim) {
foreach (var tr in gim.GenericArguments)
WalkScopeOfTypeReference (tr);
}

if (mr.HasParameters) {
WalkTypeScope (mr.Parameters);
}
}

void WalkFieldReference (FieldReference fr)
{
WalkScopeOfTypeReference (fr.FieldType);
WalkScopeOfTypeReference (fr.DeclaringType);
}

void WalkMarshalInfoTypeScope (IMarshalInfoProvider provider)
{
if (!provider.HasMarshalInfo)
return;

if (provider.MarshalInfo is CustomMarshalInfo cmi)
WalkScopeOfTypeReference (cmi.ManagedType);
}

void WalkCustomAttributesTypesScopes (ICustomAttributeProvider customAttributeProvider)
{
if (!customAttributeProvider.HasCustomAttributes)
return;

foreach (var ca in customAttributeProvider.CustomAttributes)
WalkForwardedTypesScope (ca);
}

void WalkSecurityAttributesTypesScopes (ISecurityDeclarationProvider securityAttributeProvider)
{
if (!securityAttributeProvider.HasSecurityDeclarations)
return;

foreach (var ca in securityAttributeProvider.SecurityDeclarations) {
if (!ca.HasSecurityAttributes)
continue;

foreach (var securityAttribute in ca.SecurityAttributes)
WalkForwardedTypesScope (securityAttribute);
}
}

void WalkForwardedTypesScope (CustomAttribute attribute)
{
WalkMethodReference (attribute.Constructor);

if (attribute.HasConstructorArguments) {
foreach (var ca in attribute.ConstructorArguments)
WalkForwardedTypesScope (ca);
}

if (attribute.HasFields) {
foreach (var field in attribute.Fields)
WalkForwardedTypesScope (field.Argument);
}

if (attribute.HasProperties) {
foreach (var property in attribute.Properties)
WalkForwardedTypesScope (property.Argument);
}
}

void WalkForwardedTypesScope (SecurityAttribute attribute)
{
if (attribute.HasFields) {
foreach (var field in attribute.Fields)
WalkForwardedTypesScope (field.Argument);
}

if (attribute.HasProperties) {
foreach (var property in attribute.Properties)
WalkForwardedTypesScope (property.Argument);
}
}

void WalkForwardedTypesScope (CustomAttributeArgument attributeArgument)
{
WalkScopeOfTypeReference (attributeArgument.Type);

switch (attributeArgument.Value) {
case TypeReference tr:
WalkScopeOfTypeReference (tr);
break;
case CustomAttributeArgument caa:
WalkForwardedTypesScope (caa);
break;
case CustomAttributeArgument[] array:
foreach (var item in array)
WalkForwardedTypesScope (item);
break;
}
}

void WalkScopeOfTypeReference (TypeReference type)
{
if (type == null)
return;

if (!visited.Add (type))
return;

// Don't walk the scope of windows runtime projections
if (type.IsWindowsRuntimeProjection)
return;

switch (type) {
case GenericInstanceType git:
WalkScopeOfTypeReference (git.ElementType);
foreach (var ga in git.GenericArguments)
WalkScopeOfTypeReference (ga);
return;
case FunctionPointerType fpt:
WalkScopeOfTypeReference (fpt.ReturnType);
if (fpt.HasParameters)
WalkTypeScope (fpt.Parameters);
return;
case IModifierType imt:
WalkScopeOfTypeReference (imt.ModifierType);
WalkScopeOfTypeReference (imt.ElementType);
return;
case TypeSpecification ts:
WalkScopeOfTypeReference (ts.ElementType);
return;
case TypeDefinition:
case GenericParameter:
// Nothing to walk
return;
}

markingHelpers.MarkForwardedScope (type);
}
}

}
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
using System;
using System.Diagnostics.Tracing;
using System.Diagnostics.Tracing;
using Mono.Linker.Tests.Cases.Expectations.Assertions;
using Mono.Linker.Tests.Cases.Expectations.Metadata;

namespace Mono.Linker.Tests.Cases.BCLFeatures.ETW
{
[Reference ("System.Diagnostics.Tracing.dll")]
public class CustomEventSource
{
public static void Main ()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
using System;
using System.Diagnostics.Tracing;
using System.Diagnostics.Tracing;
using Mono.Linker.Tests.Cases.Expectations.Assertions;
using Mono.Linker.Tests.Cases.Expectations.Metadata;

namespace Mono.Linker.Tests.Cases.BCLFeatures.ETW
{
[Reference ("System.Diagnostics.Tracing.dll")]
[SetupLinkerArgument ("-a", "test.exe", "library")]
[KeptMember (".ctor()")]
public class CustomLibraryEventSource
Expand Down
Loading

0 comments on commit 9ada810

Please sign in to comment.