question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Stack overflow resolving TypeReference

See original GitHub issue

In certain scenarios where a type implements an interface in another assembly, attempting to resolve the TypeReference to the interface results in an infinite loop and a stack overflow.

This is an issue I encountered with a real application with fairly complex dependencies - I’ve been struggling to find a simple repro that copies the right dlls in, but it’s possible to manually copy them to the bin folder to reproduce.

The issue occurs when the build copies the following dlls to the bin dir:

  C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.1.0\System.dll
  C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\Microsoft\Microsoft.NET.Build.Extensions\net461\lib\System.Runtime.dll
  C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\Microsoft\Microsoft.NET.Build.Extensions\net461\lib\netstandard.dll 

The latter two get copied in when you have a net framework assembly which references a net standard assembly (as per some of the details in https://github.com/dotnet/corefx/issues/26456).

You can see the issue by creating a net461 library with a single type implementing ISet<string> (e.g. https://github.com/r-bennett/MonoCecilRepro/blob/master/ClassLibrary1/Class1.cs), building the library, manually copying the above dlls to the bin folder, then attempting to load the type and resolve its interface, something like:

void Main()
{
	ResolveInterfaces(@"E:\workspace\ClassLibrary1\ClassLibrary1\bin\Debug\ClassLibrary1.dll");
}

static void ResolveInterfaces(string assemblyPath)
{
	var module = ModuleDefinition.ReadModule(assemblyPath, new ReaderParameters { AssemblyResolver = new PublishedAssemblyResolver(assemblyPath) });
	foreach (var type in module.Types)
	{
		foreach (var @interface in type.Interfaces)
		{
			@interface.Resolve();
		}			
	}
}

public class PublishedAssemblyResolver : DefaultAssemblyResolver
{
	public PublishedAssemblyResolver (string assemblyPath)
	{
		AddSearchDirectory(Path.GetDirectoryName(assemblyPath));
	}
}

Each of the 3 dlls mentioned above has only a single type (<Module>) listed on it when read with Mono Cecil, with all the expected types appearing only in exportedTypes. The issue seems to be related to net standard’s assembly unification (https://github.com/dotnet/standard/blob/315834b5eaa426ed37f6a767f15cf1d5eb7b4a85/docs/planning/netstandard-2.0/README.md#assembly-unification), although I’m not quite sure what’s going on here.

The infinite loop occurs with MetadataResolver.Resolve(TypeReference) - it tries to resolve the ISet interface first with netstandard.dll, then with System.dll, then with System.Runtime.dll, then looping back to netstandard.dll when it can’t find it in the types of any of those modules.

I’m happy to put some time into resolving this, but I’m not sure what the correct resolution behaviour should be here.

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:9 (7 by maintainers)

github_iconTop GitHub Comments

1reaction
jbevaincommented, Aug 7, 2019

Thanks to everybody who’s looking at this.

My understanding of the issue lies in the very first Resolve call here. In a .NET Core program, you’re asking the DefaultAssemblyResolver to resolve the assembly System for a program compiled for the .NET Framework and not .NET Core.

The DefaultAssemblyResolver implementation as it is today, when running on .NET Core, is going to resolve the .NET Core assembly for System, and here begins the mixup. The resolver will pick first the netstandard and System.Runtime for the .NET Framework that you provide yourself and this will never resolve properly.

I’m not sure there’s a good solution here. If you’re going to write a .NET Core program that can work on .NET Framework assemblies, the DefaultAssemblyResolver is not a good default, and you’ll need to provide your own which knows how to resolve .NET Framework references when working on a .NET Framework assembly.

1reaction
r-bennettcommented, Mar 14, 2019

@MarcoRossignoli cheers for the info - have been having a poke around with your reproducer and been finding the same things.

I tried a workaround similar to the first you suggested, although checking when the only Type on the loaded assembly was <Module> (as it affects more than just netstandard.dll) and loading the assembly from Path.GetDirectoryName(typeof(object).Assembly.Location instead. This fixed this reproduction case. There are assemblies in the the “shared” framework which have the same issue though (e.g. in your reproducer which I converted to 461/2.0, the problem dll is C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.0.7\System.dll), so it isn’t a universal fix.

The most robust solution I’ve found is to recursively add the subfolders from the GAC (under %systemroot%/assembly/...) as search directories, then Path.GetDirectoryName(typeof(object).Assembly.Location, then my assembly’s publish directory last of all - I haven’t found a project that doesn’t work for yet.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Resolving a TypeReference to a TypeDefinition in Mono. ...
Resolving a TypeReference to a TypeDefinition in Mono.Cecil fails with Assembly Resolution Error ... I'm trying to get a Mono.Cecil TypeDefinition ...
Read more >
Jackson's ObjectMapper and TypeReference - northCoder
This new TypeReference<Map<String, Object>>() {} means the map is no longer untyped. It creates the exact same end result as the first example....
Read more >
Stack Overflow is doing me ongoing harm; it's time to fix it!
It is past time for a meaningful response. I remain available to discuss the matter. Please prioritize resolving this ongoing, painful, damaging ...
Read more >
Jackson deserialize enum unknown value. #configure ...
Jackson provides the abstract class TypeReference to obtain the type information from the derivated subclasses:. no default constructor and setter, you need to ......
Read more >
Announcing OverflowAI
We are announcing our roadmap for the integration of generative AI into our public platform, Stack Overflow for Teams, and brand new product ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found