-
Notifications
You must be signed in to change notification settings - Fork 4.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Mark forwarders for all TypeReferences (dotnet/linker#2276) (dotnet/l…
…inker#2280) * 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 Commit migrated from dotnet/linker@ff06442
- Loading branch information
Showing
22 changed files
with
493 additions
and
76 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
343 changes: 343 additions & 0 deletions
343
src/tools/illink/src/linker/Linker/TypeReferenceMarker.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} | ||
|
||
} |
5 changes: 3 additions & 2 deletions
5
src/tools/illink/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/CustomEventSource.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
4 changes: 2 additions & 2 deletions
4
src/tools/illink/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/CustomLibraryEventSource.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.