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

Mark forwarders for all TypeReferences #2276

Merged
merged 6 commits into from
Sep 17, 2021
Merged
Show file tree
Hide file tree
Changes from 5 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
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 ();
sbomer marked this conversation as resolved.
Show resolved Hide resolved

// 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For future reference - this feels dangerous - not because of saving, but because of custom steps. We update the type refs in sweep step - but we don't update this one (intentionally). Again - nothing to do with this change, just that I noticed this...

}
}

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);
}
}

}
Loading