Lokad / ILPack

Serialize .NET Core assemblies

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Fail to export referenced types in `System.Private.CoreLib` due to `DefaultInterpolatedStringHandler`

Algoryx-NilsHjelte opened this issue · comments

I am getting the following exception during GenerateAssembly call:

Referenced assembly cannot be found: System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e'

This is currently from usage of the type System.Runtime.CompilerServices.DefaultInterpolatedStringHandler

I see references to System.Private.CoreLib in #139 but that issue is talking about the assemblies in the exported dll, which is a separate issue?

Ok, I have tracked this a bit more, and I see that ILPack use the netstandard lib as a proxy for System.Private.CoreLib

                .Select(assembly => assembly.Name == "System.Private.CoreLib" ? netstandardName : assembly)

The problem is that System.Runtime.CompilerServices.DefaultInterpolatedStringHandler is not part of netstandard.dll in .NET 6.0.1.

It looks like DefaultInterpolatedStringHandler is part of the System.Runtime.CompilerServices.dll, but this is not referenced by my AssemblyBuilder.

And I see no way of manually adding this reference, the AddAssemblyReference API is only available in .NET Framework

https://docs.microsoft.com/en-us/dotnet/api/system.web.compilation.assemblybuilder.addassemblyreference?view=netframework-4.8

I have also tried using the GenerateAssembly overload with IEnumerable<Assembly> referencedDynamicAssembly parameter, which I have added the required assembly dll:

      var additionalReferencedAssemblies = new[] {
        AppDomain.CurrentDomain.GetAssemblies().Single(a => a.GetName().Name == "System.Runtime.CompilerServices.Unsafe")
      };

but, this referencedDynamicAssembly is never used in the AssemblyMetadata constructor when CreateReferencedAssemblies is called.

Well, I guess this is by design, as the referencedDynamicAssembly is supposed to contain dynamic assemblies.

Anyway, I am kind of stuck here and not sure how to work around this, except for some local source code workaround in ILPack.

Hi @Algoryx-NilsHjelte, thanks for this detailed report. I can't promise much. However, if you could provide a PR for a code snippet that replicates the problem, it would help a lot. You can have a look at the content of https://github.com/Lokad/ILPack/tree/master/test/TestSubject (which is expected to match https://github.com/Lokad/ILPack/tree/master/test/Lokad.ILPack.Tests ) , and add a test that triggers the failure. This would go a long way to get this fixed.

Hi, I am unable to run the tests locally on my Mac with .NET 6.0.

I was unable to run the netcoreapp2.0 target, and updated all targets locally to net6.0.
But when I run dotnet test it just hangs, and event dotnet test --blame gives no information on what is blocking the tests.

But I have the following minimal example here that demonstrates the bug:
I have tested it in a standalone separate csproj, and not as part of ILPack.Tests

[Fact]
public void TestInterpolatedStrings() {
    var assemblyName = new AssemblyName { Name = "MyAssembly" };
    var newAssembly = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
    var newModule = newAssembly.DefineDynamicModule("MyModule");

    // Define a type with no namespace
    var myType = newModule.DefineType("MyClass", TypeAttributes.Public);

    // Define a method to just return a new instance of anonymous type.
    var myMethod = myType.DefineMethod("MyMethod", MethodAttributes.Public, typeof(string), Type.EmptyTypes);
    var generator = myMethod.GetILGenerator();
    var builder = generator.DeclareLocal(typeof(System.Runtime.CompilerServices.DefaultInterpolatedStringHandler));
    generator.Emit(OpCodes.Ldloca, builder.LocalIndex);
    generator.Emit(OpCodes.Ldc_I4, 0);
    generator.Emit(OpCodes.Ldc_I4, 0);

    var DefaultInterpolatedStringHandlerConstructorInfo = builder.LocalType.GetConstructor(new[] { typeof(int), typeof(int) })!;
    var ToStringAndClearInfo = builder.LocalType.GetMethod("ToStringAndClear", Type.EmptyTypes)!;

    generator.Emit(OpCodes.Call, DefaultInterpolatedStringHandlerConstructorInfo);
    generator.Emit(OpCodes.Ldloca, builder.LocalIndex);
    generator.Emit(OpCodes.Call, ToStringAndClearInfo);
    generator.Emit(OpCodes.Ret);

    myType.CreateType();

    SerializeAndVerifyAssembly(newAssembly, "InterpolatedStringsSerialization.dll");
}

My current workaround in the local ILPack code is to mutate the AssemblyMetadata constructor using this:

        public AssemblyMetadata(Assembly sourceAssembly, IEnumerable<Assembly> referencedDynamicAssemblies)
        {
            // ...

            var a = AppDomain.CurrentDomain.GetAssemblies().Single(a => a.GetName().Name == "System.Runtime.CompilerServices.Unsafe");
            // assemblies = assemblies.Append(a.GetName());
            AddReferencedAssembly(a.GetName().Name, a.GetName());
            _reverseForwardingMap.Add("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler", a.GetName());

            CreateReferencedAssemblies(assemblies);
        }

@vermorel This one can be closed - the related PR #161 has already merged.