krafs / Publicizer

MSBuild plugin for directly accessing non-public members in .NET assemblies.
43 stars 3 forks source link

Can't Publicize System.Private.CoreLib #101

Closed aianlinb closed 11 months ago

aianlinb commented 11 months ago

I'm finding a way to access the RuntimeHelpers.IsBitwiseEquatable<T>() and the System.SpanHelpers And the following is not working

<ItemGroup>
    <PackageReference Include="Krafs.Publicizer" Version="2.2.1">
        <PrivateAssets>all</PrivateAssets>
        <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <Publicize Include="System.Private.CoreLib" />
</ItemGroup>

Seems like it's because this special dll is not referenced directly by the project, but bind at runtime But the internal functions will not appear in the ref dlls like System.Runtime.dll Is there any way to solve it?

krafs commented 11 months ago

I've investigated a bit, and it looks difficult.

System.Runtime is a reference assembly exposing public members, but it's actually forwarding to other underlying assemblies, like System.Private.CoreLib. I couldn't find any TypeForwardedTo/From-Attributes in any of the assemblies (which I usually relate to implementation redirection). Maybe I missed something, not sure.

I also looked at the metadata for the System.Runtime ReferencePath-item in msbuild, and there's no mentioning of System.Private.CoreLib. Actually, I can't find any reference to System.Private.CoreLib in any of the msbuild props or targets.

Anyway, since System.Runtime is the assembly referenced by your project, that's the only thing you'll be able to publicize. If you could manage to find a way to reference System.Private.CoreLib directly, then I imagine you might be able to get it to work.

Sorry, I think that's all I can help you with. If you keep trying and get it working - let me know!

aianlinb commented 11 months ago

@krafs Finally, I solved it! I create a library project named System.Private.CoreLib:

<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <TargetFramework>net8.0</TargetFramework>
        <AssemblyVersion>8.0.0.0</AssemblyVersion>
        <ProduceOnlyReferenceAssembly>true</ProduceOnlyReferenceAssembly>
    </PropertyGroup>
</Project>

Write the types that I want to Publicize:

namespace System.Runtime.CompilerServices {
    public static class RuntimeHelpers {
        public static bool IsBitwiseEquatable<T>() => throw null;
    }
}

namespace System {
    public static class SpanHelpers {
        public static int IndexOfValueType<T>(ref T searchSpace, T value, int length) where T : struct, Numerics.INumber<T> => throw null;
    }
}

And reference it in my project like this:

<ItemGroup>
    <ProjectReference Include="..\System.Private.CoreLib\System.Private.CoreLib.csproj" PrivateAssets="All" Private="false" Aliases="corlib" />
</ItemGroup>

Note that the Aliases is needed to avoid CS0433 (duplicate types in different assemblies)

Finally, add the attribute to prevent runtime check:

[assembly: System.Runtime.CompilerServices.IgnoresAccessChecksTo("System.Private.CoreLib")]
namespace System.Runtime.CompilerServices {
    [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
    internal sealed class IgnoresAccessChecksToAttribute(string assemblyName) : Attribute {
        public string AssemblyName { get; } = assemblyName;
    }
}

And I can successfully use the types with extern alias without any error:

extern alias corlib;
void Test() {
    if (corlib.System.Runtime.CompilerServices.RuntimeHelpers.IsBitwiseEquatable<T>()) {
        // Do something
    }
}
krafs commented 11 months ago

That's impressive! I'm glad you got it working.

Good luck with your project!